How to stop slug generation in OctoberCMS - laravel

When I write non-unicode letters in OctoberCMS Rainlab blog title, it's converted to English letters such as this:
موضوع جديد
is converted to: modoaa-gdyd
I don't want this, I want only to replace spaces with hyphen to be for example:
موضوع-جديد
How can I do that?

Hmm for now it seems we are not able to extend js plugin for give purpose
but we can extend plugin to not use slug type
you can add this code to any of your plugin's boot method
boot method is imp
<?php namespace HardikSatasiya\DemoTest;
use System\Classes\PluginBase;
class Plugin extends PluginBase
{
public function registerComponents()
{
}
public function registerSettings()
{
}
public function boot() {
\Event::listen('backend.form.extendFieldsBefore', function($widget) {
// You should always check to see if you're extending correct model
if(!$widget->model instanceof \RainLab\Blog\Models\Post) {
return;
}
// now we remove type = slug , and use exact
// as type = slug will convert chars ARABIC_MAP to english so
$widget->fields['slug']['preset']['type'] = 'exact';
});
}
}
It will not solve your complete problem but it can simply copy your blog-title exactly in to slug, in slug text-box then you need to add / at the beginning and then you also need to place ' ' => '-' (dash at spaces) manually.
sorry this will not solve your whole issue, but just saves you from copying title to slug again and again.

Add this function as a helper in your app and reuse it
public static function generateSlug($title)
{
$title = strtolower($title);
$title = preg_replace("/[\s-]+/", ' ', $title);
$title = preg_replace("/[\s_]/", '-', $title);
return $title;
}
$slug = Helpers::generateSlug('موضوع جدید', '-');
//will produce "موضوع-جدید"
Or use this package
Sluggable Persian

I solved this problem by editing the following file:
modules\system\assets\ui\storm-min.js
I emptied the following variables:
ARABIC_MAP={},PERSIAN_MAP={}
and edited the slugify function by replacing this line:
slug=slug.replace(/[^-\w\s]/g,'')
with this:
slug=slug.replace(/[^-\w\sء-ي]/g,'')
so, now the slug accepts Arabic characters normally

Related

How can I add extra view component class paths in Laravel?

The problem
The default namespace for view components is App\View\Components with the folder being app/View/Components. I am setting up a DDD file structure and wish to do two things:
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
The new App namespace/folder setup is done via composer psr-4 autoload keys and works fine. But Laravel always used the App\View\Components namespace when trying to load components.
My attempt
I have solved the first part of my problem, but I am hoping that there is a better way. For instance when I want to move views, I can just set the view.paths config directive in my AppServiceProvider but I don't see a similar way of, essentially, adding namespaces to where Laravel looks for view components. So what I ended up doing was:
Create a ViewServiceProvider class, extending Illuminate\View\ViewServiceProvider::class and point to it in bootstrap/app.php instead
In there, override the registerBladeEngine method, in there pointing towards my own BladeCompiler class instead of the built-in one
public function registerBladeEngine($resolver)
{
// The Compiler engine requires an instance of the CompilerInterface, which in
// this case will be the Blade compiler, so we'll first create the compiler
// instance to pass into the engine so it can compile the views properly.
$this->app->singleton('blade.compiler', function () {
return new BladeCompiler(
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
In my own BladeCompiler class, which extends Illuminate\View\Compilers\BladeCompiler, override the component() and compileComponentTags() methods - basically anywhere that referenced View\\Components - with pretty much a carbon copy but instead using ViewComponents and also made sure that where they return a Illuminate\View\Compilers\ComponentTagCompiler I instead referenced my own ComponentTagCompiler
In my own TagCompiler I override the guessClassName() method, again with essentially a carbon copy, just renaming View\\Components to ViewComponents
As you can see, that's quite a lot of work just to change the path. And I also want to add another path. Multiple "apps" run under the same Laravel codebase, so for instance we might have App\Website\, App\Admin and App\Blog and, depending on which app is currently running, load a different namespace for the running app, i.e. the blog would be App\Blog\ViewComponents pointing to src/app/Blog/ViewComponents.
Is there a way to achieve this without as much overriding as above? If not, can you suggest a way to achieve the second part of the requirement?
Note: I haven't ruled out using sub folders and continuing with everything under the main App\View\Components namespace just yet - I don't want to fight Laravel more than I have to and am willing to concede if there's no better way, but if I can achieve the folder structure I want it would feel a lot tidier.
Update; got a working implementation by using a configuration and php 8 annotations
Follow the steps below to make it possible to add more lookup folders for the blade view components feature, based on your question and details you have provided. It would have helped to have posted that code you already had. But I have added a possible solution to get it to work, using Annotations and using a config with a namespace/path map.
Depending on how you switch between one application and the other, from which the details are not provided in your question, you have to modify the way the configuration is retreived in the MyComponentTagCompiler class.
Blade compiler
In order to change the ComponentTagCompiler we need to change the BladeCompiler class:
namespace App;
class YourBladeCompiler extends \Illuminate\View\Compilers\BladeCompiler
{
protected function compileComponentTags($value)
{
if (! $this->compilesComponentTags) {
return $value;
}
return (new \App\MyComponentTagCompiler( //it is about this line
$this->classComponentAliases, $this->classComponentNamespaces, $this
))->compile($value);
}
}
Service provider
Now register the YourBladeCompiler in YourViewServiceProvider :
class YourViewServiceProvider extends \Illuminate\View\ViewServiceProvider
{
public function registerBladeEngine($resolver)
{
$this->app->singleton('blade.compiler', function () {
return new \App\YourBladeCompiler( //it is about this line
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
}
MyComponentTagCompiler
This is an implemention I created that works with PHP 8 Attributes, given below:
namespace App;
#[\Attribute]
class ViewComponentName
{
public string $name;
public string $package;
public function __construct(string $name, string $package)
{
$this->name = $name;
$this->package = $package;
}
}
With this attribute, you can declare the package name and component name on the view component class (see example at the bottom). So during lookup the component can be matched on these parameters.
But you can change it to your own requirements if needed.
What it does:
It first let's Laravel lookup the View Component through it's own mechanisms, in the parent::componentClass method.
If no component is found and an exception (InvalidArgumentException) is thrown, after which my implementation will walk through the given paths and namespaces (from the getLookupPaths method) and see if an attribute matches the component name and package name. If so it returns this class and the view component is loaded accordingly.
namespace App;
use App\View\ViewComponentName;
use Illuminate\View\Compilers\ComponentTagCompiler;
class MyComponentTagCompiler extends ComponentTagCompiler
{
protected function getLookupPaths() : array
{
/*
* add some logic here to get an application specific configuration
* since you have multiple application in one, I cannot know it works in your
* application, since the details are not provided in the question
*/
return config('view_component_paths');
}
private function getFiles(string $dir) : array
{
return scandir($dir);
}
private function isPhpFile(string $file) : bool
{
return strpos($file, ".php");
}
private function getClassNamespace(string $file, string $folderNamespace) : string
{
$class = str_replace(".php", "", $file);
$classNamespace = $folderNamespace . "\\" . $class;
return $classNamespace;
}
private function getComponentName(string $file, string $namespace) : ?ViewComponentName
{
$classNamespace = $this->getClassNamespace($file, $namespace);
$reflection = new \ReflectionClass($classNamespace);
if(method_exists($reflection, 'getAttributes')) {
$attribute = $reflection->getAttributes()[0];
if ($attribute->getName() == ViewComponentName::class) {
return $attribute->newInstance();
}
}
return null;
}
public function componentClass(string $component)
{
try {
parent::componentClass($component);
} catch(\InvalidArgumentException $e) {
list($lookupComponentPackage, $lookupComponentName) = explode("-", $component);
foreach($this->getLookupPaths() as $namespace=>$dir) {
foreach ($this->getFiles($dir) as $file) {
if ($this->isPhpFile($file)) {
if($componentName = $this->getComponentName($file, $namespace)) {
if($componentName->name == $lookupComponentName && $componentName->package == $lookupComponentPackage) {
return $this->getClassNamespace($file, $namespace);
}
}
}
}
}
throw $e;
}
}
}
Where the config contains (config/view_component_paths.php):
return [
"App\\Test"=>__DIR__ . "/Test/"
];
If you wish to replace the default laravel behavior completely or do not like my implementation based on annotations, consider implementing your own version of the method:
public function componentClass(string $component)
{
//return the class name here based the component name
//without calling parent
dd($component);
}
Example view component
namespace App\Test;
use App\View\ViewComponentName;
use Illuminate\View\Component;
#[ViewComponentName('test', 'namespace')]
class MyViewComponent extends Component
{
public function render()
{
return view('components.test');
}
}
In blade:
<x-namespace-test />
It should now be working. I think this is enough information to give you an idea of how to implement this in your own application. There seems to be no other way than to extend some base classes. But looking at this answer, it is possible to create a high level implementation based on a global lookup configuration and php annotations (or some other mechanism you wish, for example converting the class name with namespace to a view component name).
Old answer
Problem 2 as defined in your question
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
Sadly there seems to be no way of defining multiple class paths for view components in Laravel. But you can however change the application path and namespace prefix. As far as I found out you only have to overwrite the following properties in the Application class.
bootstrap/app.php
Replace the following lines:
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
With:
class YourApplication extends \Illuminate\Foundation\Application
{
protected $namespace = "App\\MyApplication";
protected $appPath = __DIR__ . "/../app/MyApplication";
}
$app = new YourApplication(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
This is enough to change the app folder to another and gives you an idea of how to change it dynamically for having multiple apps in different namespaces. If you now run laravel commands like php artisan make:component Test1234 it is created in your new app folder: app/MyApplication/View/Components/Test1234.php.
Hardcoded paths
Some paths like View/Components are hardcoded in Laravel, and therefor not that easy to change. If you change as defined above, in this case the view components namespace becomes: App\MyApplication\View\Components and the path: app/MyApplication/View/Components.
Problem 1 as defined in your question
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
When you change application paths as explained above it is not possible to have a "shared" View Component folder. Laravel, as it seems, has only one default View Components path, which is based on hard coded paths and a dynamic namespace prefix as explained above. But you can of course, create a shared namespace and register the view components manually:
View component (app/ViewComponents/ folder)
namespace App\ViewComponents;
use Illuminate\View\Component;
class Test extends Component
{
public function render()
{
return view('components.test');
}
}
Don't forget the components.test blade view.
ServiceProvider
\Blade::component("shared-test",\App\ViewComponents\Test::class);
Blade
<x-shared-test />

Magento $this->__('Create an Account')

In Magento $this->__('Create an Account') How this echo Create an Account?
abstract class Mage_Core_Helper_Abstract{ public function __()
{
$args = func_get_args();
$expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->_getModuleName());
array_unshift($args, $expr);
return Mage::app()->getTranslator()->translate($args);
}
I saw that __ function in Mage_Core_Helper_Abstract Class.but i cant understand Mage::app()->getTranslator()->translate($args) what's happending in that getTranslator function.
public function getTranslator()
{
if (!$this->_translator) {
$this->_translator = Mage::getSingleton('core/translate');
}
return $this->_translator;
}
Mage::getSingleton('core/translate') what's happening there ? and why in this function call like core/translate which file its denote and how it Create an Account text?
You might search how the magento translator works
What ever text is written in $this->_('') will dynamically translated to the current locale which is loaded in your current store(That text must be specified in magento-root/app/locale//.csv)
I think the below answer might be helpfull
How does Magento translate works?

Displaying database columns based on current language

in database i have this:
name_en | name_fr
When the user select french language - for example - i want to get name_fr field, and the same thing if he chose another language
Assuming you set locale in your application using:
App::setLocale($lang);
if you use Eloquent, you can add to your model class accesssor:
public function getNameAttribute($value) {
return $this->{'name_'.App::getLocale()};
}
and also mutator:
public function setNameAttribute($value) {
$this->{'name_'.App::getLocale()} = $value;
}
Assuming you added those functions to Content model you can now use:
$content = Content::first(); // find first article
echo $content->name; // displaying its name
$content->name = 'updated content'; // changing its name
$content->save(); // saving
This will cause displaying and changing name_{$lang} if you set lang using setLocale

Create "Permalink" functionality in codeigniter

Recently i am working with codeigniter. I need to create a permalink functionality just like as wordpress. Can anyone suggest me a way that, how i can implement this in codeigniter.
Can you give more details about what you want to do.
In my understanding you want to create something like this?
http://example.com/2012/post-name/
1) You must have a mod_rewrite.
2) Use the controller or model but will advice to use Controller
Controller:
public function something(string name, int year){
//some code
}
This will be translated to
http://www.example.com/something/name/year
Hope this helps or you can paste your code so I can explain further.
So create the page: admin.php
Then in the controller:
public function about-us(){
$this->view->load('about-us');
}
When you run the page is will be: http://domain.com/about-us. about-us is the function in the controller.
Are you asking how to remove the spaces and replace with a hyphen?
$slug = 'this is a bad slug';
$fixed_slug = str_replace(' ', '-', $slug);
# uncomment next line to see result
# echo $fixed_slug;
If you want to make that a method, no worries:
public function fix_slug($slug){
$fixed_slug = str_replace(' ', '-', $slug);
return $fixed_slug;
}
You can obviously do more in that method, or abstract it to use with other text you want altered.

CodeIgniter URL_TITLE model?

Here is example of URl_title CI, i know this code is do this
$title = "Whats wrong with CSS";
$url_title = url_title($title, '_', TRUE);
// Produces: whats_wrong_with_css
But hot to revers, is there a function in Ci to reverse something like this and return the true value?
like this ?
// Produces: Whats wrong with CSS
hi you can do it just with simple way
$title = ucfirst(str_replace("_",' ',$url_tilte));
echo $title;
I would "extend" CI's URL helper by creating a MY_url_helper.php file in application/helpers and create a function similar to what umefarooq has suggested.
/*
* Un-create URL Title
* Takes a url "titled" string as de-constructs it to a human readable string.
*/
if (!function_exists('de_url_title')) {
function de_url_title($string, $separator = '_') {
$output = ucfirst(str_replace($separator, ' ', $string));
return trim($output);
}
}
Providing you have loaded the url helper, you will then be able to call this function throughout your application.
echo de_url_title('whats_wrong_with_css'); // Produces: Whats wrong with css
The second ($separator) paramater of the function allows you to convert the string dependent on whether it's been "url_title'd" with dashes - or underscores _

Resources