I am trying to set up a Drupal 6 node to load blocks dynamically depending on the selected theme. I figured that I could use $theme_key to determine the name of the theme, and work from there. The weird thing is that if I have several checks on a page, the first one will return an empty value for $theme_key, but subsequent checks will work as expected.
For instance:
<?php
print "Theme: ". $theme_key;
if($theme_key =="foo"){
$viewName = 'theView';
$display_id = 'block_1';
print views_embed_view($viewName, $display_id);
}
else {
$viewName = 'theOtherView';
$display_id = 'block_1';
print views_embed_view($viewName, $display_id);
}
?>
If I have the above in the node multiple times with theme "foo" active (for testing purposes) - the first time will return a blank value for $theme_key, and display theOtherView, but the second time it will show Theme: foo and will display theView. All subsequent calls to $theme_key will be correct as well.
Any calls to $theme_key prior to the first block will return blank values.
I am declaring
<?php global $theme_key; ?>
at the beginning of the node content. (Before all of the conditional blocks...)
What am I doing wrong? Is there a better way to check the current theme?
Just looking at the init_theme() function the globals $theme and $theme_key contain exactly the same values so you could try using $theme (as yvan suggested), but as they contain the same data and are both globals set in the same function I'm not sure it would make any difference.
Is this code in a template file? If so you could be suffering from the age old Drupal theming problem whereby some variables aren't available when the template is built. You could try adding a hook_preprocess_node() function in a module/theme to set up the variable and pass it to your template file. Something like this:
function MYMODULE_preprocess_node(&$vars) {
global $theme_key;
$vars['current_theme_key'] = $theme_key;
}
And then in your template file you'll have access to the variable $current_theme_key, which should then have the right variable in it.
Hope that helps, I've come across these sort of problems with Drupal before and they're a nightmare to debug.
Edit to add more helpful function:
function MYMODULE_preprocess_node(&$vars) {
$node = $vars['node'];
if ($node->type == 'my_type') {
global $theme_key;
$view_name = $theme_key == 'foo' ? 'theView' : 'theOtherView';
$display_id = 'block_1';
$vars['my_custom_view'] = views_embed_view($view_name, $display_id);
}
}
Then in your node.tpl.php or node-type.tpl.php file you can use code like this:
if (isset($my_custom_view)):
echo $my_custom_view;
endif;
By constructing the variables in a preprocess function you shouldn't have any problems with the global $theme_key not being available any more.
Bear in mind you could also implement this in your theme (in the template.php file) by changing the function name to MYTHEME_preprocess_node.
Make sure you clear your Drupal cache once you've implemented the function (wherever you decide to put it) so the system will pick up the changes.
Hope that helps!
Related
I was creating a wordpress plugin where the user enters in some information, and is able to generate shortcodes. I was not quite sure where the shortcodes are supposed to go - my current setup is class-based, and I want to be able to create a shortcode when an AJAX request is being made, and is successful. The following two methods are in the same file in the class.
This method gets called via the admin-ajax.php file:
public static function processAjax()
{
global $wpdb;
$event_data = $_POST['obj'];
$event_title = $event_data[0];
$event_subdomain = $event_data[1];
$result_events = $wpdb->get_results("SELECT * FROM wp_shortcode_plugin WHERE subdomain = '{$event_subdomain}'", OBJECT);
if (sizeof($result_events)>0) {
echo "duplicate";
} else {
add_shortcode($event_subdomain, 'getEmbed');
$results = $wpdb->insert('wp_shortcode_plugin', array("event_name"=>$event_title, "subdomain"=>$event_subdomain));
echo json_encode($_POST['obj']);
}
die();
}
And here is my getEmbed() method that I would like to call.
public static function getEmbed()
{
return 'test';
}
It seems that the shortcodes are not being created, however. Am I missing something here? Also, is it possible to pass a value to the getEmbed function from the add_shortcode() method?
Instead of adding shortcode directly from AJAX, you should use update_option to store in the information for the shortcode to be loaded. If option doesn't exist, it will be created.
Than you will simple use wp_init hook to load up all of the shortcodes you need to load in the function.php file for the theme or plugin php file.
You should use get_option within the wp_init hook and check the values in there. You will need to have function(s) associated with the shortcodes, which can be autogenerated in php using create_function or you can route them through 1 function (defined in your php file) that will have the $atts and $content parameters defined and do whatever depending on the value of your get_option that you send to that function.
add_shortcode function should be defined within the wp_init hook, after checking the value of the get_option function. You will need to give your option a name and add to it via the ajax function. the option will most likely want to be an array, that wordpress will automatically serialize. Than you use that array returned from get_option to loop through the array of shortcodes, and call add_shortcode as many times as you need there. This requires setting up your option array so that it has a shortcode tag defined in each index of the array. I would, personally, make the shortcode tag the key of the array and all attributes of the shortcode, imo, would than be an array of that array.
Hope this helps you to get started on this.
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.
I have an issue with triming a field before it is saved. I wanted to use substr(), or regex() with preg_match(). I have built a Drupal 7 module, but it can't work at all. I have tried using the trim plugin in feeds tamper module, but it doesn't seem to work. The data I am using is from a feed from Google Alerts. I have posted this issue here.
This is what I have done so far, and I know my regular expression is wrong; I was trying to get it do anything, just to see if I could get it to work, but I am pretty lost on how to add this type of function to a Drupal module.
function sub_node_save() {
$url = $node->field_web_screenhot['und'][0]['url'];
$url = preg_match('~^(http|ftp)(s)?\:\/\/((([a-z0-9\-]*)(\.))+[a-z0-9]*)($|/.*$)~i',$url );
$node->field_web_screenhot['und'][0]['url'] =$url;
return ;
}
I used the Devel module to get the field.
If there's an easy way to use substr(), I would consider that or something else.
Basically, I just want to take the Google redirect off the URL, so it is just the basic URL to the web site.
Depending on your question and later comments, I'd suggesting using node_presave hook (http://api.drupal.org/api/drupal/modules!node!node.api.php/function/hook_node_presave/7) for this.
It's called before both insert (new) and update ops so you will need extra validations to prevent it from executing on node updates if you want.
<?php
function MYMODULE_node_presave($node) {
// check if nodetype is "mytype"
if ($node->type == 'mytype'){
// PHP's parse_url to get params set to an array.
$parts = parse_url($node->field_web_screenhot['und'][0]['url']);
// Now we explode the params by "&" to get the URL.
$queryParts = explode('&', $parts['query']);
$params = array();
foreach ($queryParts as $param) {
$item = explode('=', $param);
$params[$item[0]] = $item[1];
}
//valid_url validates the URL (duh!), urldecode() makes the URL an actual one with fixing "//" in http, q is from the URL you provided.
if (valid_url(urldecode($parms['q']))){
$node->field_web_screenhot['und'][0]['url'] = urldecode($parms['q']);
}
}
}
I am using Drupal 6.16 with a number of modules installed. I was trying to find out if there is a way to change the output of a node when a different file extension is added to the url. For example:
http://example.com/drupal?q=foo/bar - returns a normal drupal node
http://example.com/drupal?q=foo/bar.xml - returns xml output of the node
Is this even possible with Drupal? Do I have to hack the core code to get this working?
You should not need to hack the core code. There are probably several contributed modules that can do this for you.
To output an XML version of a node, check out the Views Bonus Pack module, which extends the Views module. It has basic export capabilities, including CSV, TXT, DOC, and XML. The documentation is brief, but there is a README.txt file in the views_bonus/export/ directory that gives the basic steps for creating a feed in a view that will output XML.
You can set the path for the feed, so while I don't believe the .xml extension will work, you could set up a path with an additional component like this:
http://example.com/drupal?q=foo/bar <-- normal output
http://example.com/drupal?q=foo/bar/xml <-- XML output
To change the template file that is used for a node based on the path, you can use a preprocess function in your template.php file to add a template suggestion based on the path. This takes a bit more understanding of how the template files work, but ultimately you'll have more control of the output than you will with a view.
Here is how I fixed this.
Add the custom_url_rewrite_inbound function to check for incoming request ending with .xml. If it finds a request ending with .xml it strips that off, so that the correct data can be located by the rest of the drupal machinery. It also sets 'subsite_xml_request' to true so that the appropriate theme template can be used later.
function custom_url_rewrite_inbound (&$result, $path, $path_language) {
if(preg_match('/\.xml$/', $path)) {
$search = preg_replace('/^(.*)\.xml$/', "$1", $path);
if ($src = drupal_lookup_path('source', $search, $path_language)) {
$_REQUEST['xml_request'] = true;
$result = $src;
}
}
Modify the phptemplate_preprocess_page function in your template.php to add additional '-xml' templates.
function phptemplate_preprocess_page(&$vars) {
if ($_REQUEST['xml_request']) {
if (module_exists('path')) {
$path = str_replace('/edit','',$_GET['q']);
$alias = drupal_get_path_alias($path);
if ($alias != $_GET['q']) {
$template_filename = 'page';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '-' . $path_part;
$vars['template_files'][] = $template_filename . '-xml';
}
$vars['template_files'][] = 'page-xml';
}
}
}
}
Create the required page-xml.tpl.php
Using the autocomplete field for a cck nodereference always displays the node id as a cryptic bracketed extension:
Page Title [nid:23]
I understand that this ensures that selections are unique in case nodes have the same title, but obviously this is a nasty thing to expose to the user.
Has anyone had any success in removing these brackets, or adding a different unique identifier?
Ultimately, you need to change the output of nodereference_autocomplete() in nodereference.module.
To do this properly, you want a custom module to cleanly override the function.
This function is defined as a menu callback, thus,
/**
* Implementation of hook_menu_alter().
*/
function custom_module_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'custom_module_new_nodereference_autocomplete';
}
Then, copy the nodereference_autocomplete function into your custom module, changing it's name to match your callback. Then change this one line:
$matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
Dropping the nid reference.
$matches[$row['title']] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
I believe the identifier is purely cosmetic at this point, which means you could also change the text however you like. If it is not purely cosmetic, well, I haven't tested to see what will happen in the wrong conditions.
I always meant to identify how to do this. Thank you for motivating me with your question.
What Grayside has posted will work... as long as you don't have two nodes with the same title. In other words, if you want to do as Grayside has proposed, you need to be aware that the nid is not entirely unimportant. The nodereference_autocomplete_validate() function does two things. It checks to see if there is a node that matches, and if so, it passes the nid on, setting it to the $form_state array. If it can't find a node, it will set an error. If the nid is present, it will be used to get the node, which also is faster, the code is here:
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
if (!empty($matches)) {
// Explicit [nid:n].
list(, $title, $nid) = $matches;
if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));
}
}
This just checks to see if there is a nid and checks if that node matches with the title, if so the nid is passed on.
The 2nd option is a bit slower, but it is here errors can happen. If you follow the execution, you will see, that if will try to find a node based on title alone, and will take the first node that matches. The result of this, is that if you have two nodes with the same title, one of them will always be used. This might not be a problem for you, but the thing is, that you will never find out if this happens. Everything will work just fine and the user will think that he selected the node he wanted to. This might be the case, but he might as well have chosen the wrong node.
So in short, you can get rid of the nid in the autocomplete callback, but it has 2 drawbacks:
performance (little)
uncertainty in selecting the correct node.
So you have to think about it, before going this route. Especially, since you most likely wont be able to find the problem of the selection of the wrong nodes, should it happen. Another thing to be aware of, is that the nid showing up, also brings some valuable info to the users, a quick way to lookup the node, should they be in doubt if it is the one they want, if several nodes have similar titles.
I got Grayside's answer to work, but I had to use MENU alter, instead of the FORM alter he posted. No biggy!
function custommodule_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'fp_tweaks_nodereference_autocomplete';
}
I've found an alternative solution is to change your widget type to select list and then use the chosen module to convert your list to an autocomplete field.
This handles nodes with the same title, and actually I think the UI is better than the one provided by the autocomplete widget.
To anyone coming across this (rather old) topic by way of a google search - for Drupal 7 please consider using entityreference module and "Entity Reference" field type if possible.
You can acheive a lot more in configuration with an "Entity Reference" field. It doesn't have this problem with the nid in square brackets.
Here is the full Drupal 7 version (References 7.x-2.1) of Grayside's answer. This goes in your custom module:
/**
* Implementation of hook_menu_alter().
*/
function custom_menu_alter(&$items) {
$items['node_reference/autocomplete/%/%/%']['page callback'] = 'custom_new_node_reference_autocomplete';
}
/**
* Implementation of Menu callback for the autocomplete results.
*/
function custom_new_node_reference_autocomplete($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
$options = array(
'string' => $string,
'match' => $instance['widget']['settings']['autocomplete_match'],
'limit' => 10,
);
$references = node_reference_potential_references($field, $options);
$matches = array();
foreach ($references as $id => $row) {
// Markup is fine in autocompletion results (might happen when rendered
// through Views) but we want to remove hyperlinks.
$suggestion = preg_replace('/<a href="([^<]*)">([^<]*)<\/a>/', '$2', $row['rendered']);
// Add a class wrapper for a few required CSS overrides.
$matches[$row['title']] = '<div class="reference-autocomplete">' . $suggestion . '</div>'; // this is the line that was modified to remove the "[nid:XX]" disambiguator
}
drupal_json_output($matches);
}