Drupal 8 hook_theme_suggestions_HOOK_alter() not being used - drupal

I'm using hook_theme_suggestions_HOOK_alter() to add theme hook suggestions for page.html.twig based on the content type:
function mytheme_theme_suggestions_page_alter(array &$suggestions, array $variables) {
$node = \Drupal::request()->attributes->get('node');
$suggestions[] = 'page--node--' . $node->getType();
}
Now my twig debug mode picks this up:
<!-- FILE NAME SUGGESTIONS:
* page--node--case.html.twig (new suggestion based on content type 'case')
* page--node--3.html.twig
* page--node--%.html.twig
* page--node.html.twig
x page.html.twig
-->
However, when I make a file called page--node--case.html.twig, it is not being rendered. Instead, page.html.twig is being used.
Anyone know what's going on?

I found out what was going wrong.
Apparently, when defining new suggestions, Drupal needs underscores instead of dashes. Then Drupal converts these underscores into dashes so that the actual file name will still be page--node--case.html.twig
So:
$suggestions [] = 'page--node--'.$node->gettype();
Should be: $suggestions [] = 'page__node__'.$node->gettype();
Documentation:
https://www.drupal.org/node/2186401

Related

Get localized string for specific locale

How can I use the localization mechanism in WordPress to get access to an existing but not-current language string?
Background: I have a custom theme where I use locale 'en_US' as the default locale and translate through a PO file to locale 'es_ES' (Spanish).
Let us say I use the construction
__('Introduction', 'my_domain');
in my code, and that I have translated 'Introduction' to the Spanish 'Introducción´ in my PO file. All this works fine.
Now to the problem: I want to insert n records in my database with all existing translations of the string 'Introduction' - one for each language; so, n = 2 in my example.
Ideally, I would write something like this:
$site_id = 123;
// Get an array of all defined locales: ['en_US', 'es_ES']
$locales = util::get_all_locales();
// Add one new record in table details for each locale with the translated target string
foreach ($locales as $locale) {
db::insert_details($site_id, 'intro',
__('Introduction', 'my_domain', $locale), $locale);
}
Only, that the 3rd parameter in __() above is pure fantasy on my part. You can only validly write
__('Introduction', 'my_domain');
to get either 'Introduction' or 'Introducción' depending on the current locale.
The outcome of the code above would ideally be that I end up with two records in my table:
SITE_ID CAT TEXT LOCALE
123 intro Introduction en_US
123 intro Introducción es_ES
I am aware that I want something that requires loading all the MO files, where normally, only the MO file for the current language is required. Maybe use of the WordPress function load_textdomain is necessary - I was just hoping there already exists a solution.
Expanding on the question by including the plugin PolyLang: is it possible to use Custom Strings to achieve the above functionality? E.g. conceptually:
pll_('Introduction', $locale)
Old question I know, but here goes -
Starting with a simple example where you know exactly what locales to load and exactly where the MO files are, you could use the MO loader directly:
<?php
$locales = array( 'en_US', 'es_ES' );
foreach( $locales as $tmp_locale ){
$mo = new MO;
$mofile = get_template_directory().'/languages/'.$tmp_locale.'.mo';
$mo->import_from_file( $mofile );
// get what you need directly
$translation = $mo->translate('Introduction');
}
This assumes your MO files are all under the theme. If you wanted to put more of this logic through the WordPress's environment you could, but it's a bit nasty. Example:
<?php
global $locale;
// pull list of installed language codes
$locales = get_available_languages();
$locales[] = 'en_US';
// we need to know the Text Domain and path of what we're translating
$domain = 'my_domain';
$mopath = get_template_directory() . '/languages';
// iterate over locales, finally restoring the original en_US
foreach( $locales as $switch_locale ){
// hack the global locale variable (better to use a filter though)
$locale = $switch_locale;
// critical to unload domain before loading another
unload_textdomain( $domain );
// call the domain loader - here using the specific theme utility
load_theme_textdomain( $domain, $mopath );
// Use translation functions as normal
$translation = __('Introduction', $domain );
}
This method is nastier because it hacks globals and requires restoring your original locale afterwards, but it has the advantage of using WordPress's internal logic for loading your theme's translations. That would be useful if they were in different locations, or if their locations were subject to filters.
I also used get_available_languages in this example, but note that you'll need the core language packs to be installed for this. It won't pick up Spanish in your theme unless you've also installed the core Spanish files.

Generating doctrine slugs manually

I'm using sluggable behavior in my Symfony2 project, but now I would like to make many slugs for one page, based on different texts (current title, old title(s), users text from form input), and keep it in another table. And my question is - how to manually use doctrine extensions for any text? I can't find it anywhere. Perfect would be something like:
/* careful - it's not a real, working code! */
$sluggable = new DoctrineSluggable();
$slug = $sluggable->generate('My own text!');
echo $slug; // my-own-text
I found solution by accident here.
Code:
use Gedmo\Sluggable\Util as Sluggable;
$string = 'My own text!';
$slug = Sluggable\Urlizer::urlize($string, '-');
if(empty($slug)) // if $string is like '=))' or 'トライアングル・サービス' an empty slug will be returned, that causes troubles and throws no exception
echo 'error, empty slug!!!';
else
echo $slug;
Find the doctrine code for generating a slug here: l3pp4rd/DoctrineExtensions. Playing around with that class could do as you desire but you will probable need to create your own service to implement an easy use as you want. See the Service Container section of the docs for more details about services.
The Sluggable\Urlizer::urlize seems to replace ' with -.
I had to use Sluggable\Urlizer::transliterate to be closer to the SluggableListener behaviour.

Drupal path wildcard but not on all levels

I have created a view page which shows all the work project thumbnails by default and then there is a block included on the page which filters the view (but not using ajax) it just added the company name into the url and it filters using contexual filters. The thumbnails in the grid take you to the project node page. I would like to include the filter sidebar block on all of the urls under www.sitename.com/work, so work & work/companyname/ but not on the node page which is www.sitename.com/work/companyname/projectname.
I have tried all possible ways of doing it within the path field.
work
work/*
work/*/~
work/*/~/
work/*/~/~
Is there anyway to include this block on all paths work/companyname but not any deeper?
You could enable the core PHP Filter module. Then you can use php to set the block visibility e.g. using preg_match().
If you're not sure about regex take a look at http://www.regextester.com/.
EG to show block in admin and admin/structure but not admin/structure/blocks etc:
<?php
return preg_match('/^admin(\/structure)?(\/)?$/', $_GET['q']);;
?>
What you are looking for is support for globbing (https://github.com/begin/globbing#wildcards). Unfortunately, Drupal does not support globbing out of the box.
In modern globbing implementation, * would match on any character but /, and ** would match on any character, including /.
In order to implement this support, one would need to:
Look how PathMatcher (core/lib/Drupal/Core/Path/PathMatcher.php) service matches the path.
Extend it into own custom service where only matchPath() will be overridden.
Replace the content of matchPath() with the code below (this is a copy of the original matchPath() with some alterations).
Alter included service to use your custom path matching (for block only or whole site).
Update configuration of blocks to use ** for full path matching and * for subpath only.
/**
* {#inheritdoc}
*/
public function matchPath($path, $patterns) {
if (!isset($this->regexes[$patterns])) {
// Convert path settings to a regular expression.
$to_replace = [
// Replace newlines with a logical 'or'.
'/(\r\n?|\n)/',
'/\\\\\*\\\\\*/',
// Quote asterisks.
'/\\\\\*/',
// Quote <front> keyword.
'/(^|\|)\\\\<front\\\\>($|\|)/',
];
$replacements = [
'|',
'.*',
'[^\/]*',
'\1' . preg_quote($this->getFrontPagePath(), '/') . '\2',
];
$patterns_quoted = preg_quote($patterns, '/');
$this->regexes[$patterns] = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
}
return (bool) preg_match($this->regexes[$patterns], $path);
}
Note that this code only adds additional token replacement ** and alters what * token does (any character but /).

Getting the current module name

Is there a way to get the name of the module you are working within? I have a large set of modules (about 35) with some common functionality. Long story short is that I would like to be able to get the module name without hard-coding it in a string. Hopefully this isn't necessary, but here's an idea of what I'm trying for:
function MYMODULE_mycustom_hook($args) {
$sCurrModule = 'MYMODULE';
// Operations using $sCurrModule...
}
Essentially, I can replace 'MYMODULE' with the module name and be done with it, but I'm wondering if there is a way to get that value programmatically. I'm using Drupal 7.
This does not apply to Drupal 8.
If your module file is sites/default/modules/MYMODULE/MYMODULE.module then module name is MYMODULE.
You can get it programmatically inside MYMODULE.module file using following command:
$module_name = basename(__FILE__, '.module');
Although OP was asking regarding D7, here's the solution for Drupal 8 (D8) as well:
/** #var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
$module_handler = \Drupal::service('module_handler');
/** #var \Drupal\Core\Extension\Extension $module_object */
$module_object = $module_handler->getModule(basename(__FILE__, '.module'));
$module_name = $module_object->getName();
Of course, you can chain these calls if necessary:
\Drupal::service('module_handler')->getModule(basename(__FILE__, '.module'))->getName()
As correctly said in comments
basename(__FILE__, '.module');
only works inside an actual .module file.
Answering to question:
inside your MYMODULE.module
function print_current_module_name() {
return basename(__FILE__, '.module');
}
from everywhere in Drupal >8
$module_name = print_current_module_name();
echo "module name is : " . $module_name
Not sure this will work on Drupal 7 as well

preprocess function for cck node types

(Note: I originally posted this on drupal.org before remembering that I never get a response over there. So, sorry for the cross-posting)
Hello, is there a way (built-in or otherwise) to add preprocessing functions for particular cck node types? I am looking to do some preprocessing of a field within my cck node type. Currently I can either use theme_preprocess_node and then do a switch on the $node->type or use a theming function for a particular field name (and still do a switch to make sure the current field usage is within the node type i'm looking for). What I am suggesting is to have a function like this...
theme_preprocess_mynodetype(&$vars) {
// Now I can preprocess a field without testing whether the field is within the target content type
}
...but I can't figure out if I can suggest preprocess functions the same way I can suggest template files
Thanks! Rob
See this function in content.module of cck:
/**
* Theme preprocess function for field.tpl.php.
*
* The $variables array contains the following arguments:
* - $node
* - $field
* - $items
* - $teaser
* - $page
*
* #see field.tpl.php
*
* TODO : this should live in theme/theme.inc, but then the preprocessor
* doesn't get called when the theme overrides the template. Bug in theme layer ?
*/
function content_preprocess_content_field(&$variables) {
$element = $variables['element'];
...
I think that you're looking for this post. There's no magic per-node preprocess, only per theme/template engine, but you do have access to the node type in the $vars parameter so you can switch on it there.
Hope that helps!

Resources