How to change the node preview title to be displayed instead of just "Preview"?
This is a way to solve your problem in your custom module
/**
* implements hook_form_BASE_FORM_ID_alter
* the form id that will build the node preview is page_node_form
* #param $form
* #param $form_state
*/
function yourmodulename_form_page_node_form_alter( $form, $form_state ){
if( !empty( $form_state['node']->in_preview ) ){
// security hint: do not pass the PASS_THROUGH param to the drupal_set_title
// because the node title may contain some xss. Without this parameter the
// drupal_set_title will check for xss and remove them if present
drupal_set_title(t('Preview') . ' ' . $form['#node']->title );
}
}
Here how to hack core to modify the title (quick and easy way) but better would be to override via a custom module (perhaps somebody else can post please).
In /modules/node/node.pages.inc add $node->title within drupal_set_title(t('Preview'), PASS_THROUGH);
like so:
// Display a preview of the node.
if (!form_get_errors()) {
$cloned_node->in_preview = TRUE;
$output = theme('node_preview', array('node' => $cloned_node));
unset($cloned_node->in_preview);
}
drupal_set_title(t('Preview: '.$node->title), PASS_THROUGH);
Related
I am currently developing for a site that uses Timber in Wordpress. I am using an API of a different site to fetch posts from a different site, so they are up to date with the current state on that site. The problem is that I’m using the post title field to fetch the correct ID from the API. This means that no title or content data is stored in the database.
Is there a way I can register this data so that Timber PostQuery objects will also fetch these pages properly? I am unable to access or alter the result of $result = new Timber\PostQuery() afterwards, since these fields are protected and private.
Thanks in advance!
#Stan this is definitely an edge edge edge case. If you can get what you need down to WordPress IDs, these can be sent straight to PostQuery()...
$result = new Timber\PostQuery(array(2342, 2661, 2344, 6345,));
You could try extending the PostQuery class yourself to see if this is custom functionality you could wrap inside of it so the API you end up working with at the top-level is clean and simple
Timber is designed to be extended and customised to your use case.
You can create a custom class that extends Timber\Post and write your own methods for fetching the data from the API as necessary.
<?php
class CustomPost extends \Timber\Post {
/* not necessary, this just caches it */
private $myCustomContent;
/* override Timber\Post::content */
public function content(){
/* if we've fetched it from the API this request, return the result */
if ( $this->myCustomContent ) return $myCustomContent;
/* otherwise fetch it, then return the result */
return $this->fetchCustomContent();
}
/* function to fetch from external API */
private function fetchCustomContent(){
/* whatever the API call is here.. */
$result = wp_remote_post....
/* maybe some error handling or defaults */
/* cache it on the object's property we setup earlier */
$this->myCustomContent = $result->content;
return $this->myCustomContent;
}
}
Now to use our custom class we have two choices. We can manually decide when to use it by specifying it as the 2nd argument in our PostQuery()
<?php
/* Note: Passing in 'null' as the first argument timber uses the global / main WP_Query */
$items = new PostQuery( null, CustomPost::class );
/* This examples is a custom query (not the global / main query ) */
$args = [
'post-type' => 'my-custom-post-type',
// etc etc
];
$items = new PostQuery( $args, CustomPost::class );
/* Each Post in $items will be the class CustomPost instead of Timber\Post */
If your custom post classes correspond with a particular post type, you can use the Timber Class Map to always get back the corresponding Custom Post Class..
<?php
/* functions.php or similar */
add_filter( 'Timber\PostClassMap', 'my_func_modify_class_map' );
function( $classMap ){
$classMap['my-custom-post-type'] = CustomPost::class;
return $classMap;
}
/* index.php or xxx.php template file... */
$args = [
'post-type' => 'my-custom-post-type',
// etc etc
];
/* No second argument needed */
$items = new PostQuery( $args );
/* Each Post in $items will be the class CustomPost instead of Timber\Post */
Hope this helps!
Does anyone know a way to programmatically render a view from a module using the default theme after editing a node?
I'm basically trying to create a static html page of a view.
I have the following code in a custom module:
function MODULENAME_node_update($node) {
unset($node->is_new);
unset($node->original);
entity_get_controller('node')->resetCache(array($node->nid));
$view = views_get_view('references');
$view->set_display('block');
$output = $view->render();
file_put_contents('references.html', $output);
}
The code works but for obvious reasons it renders the view using the admin theme.
I have tried several things to no avail:
variable_set
function MODULENAME_node_update($node) {
variable_set('admin_theme', 'DEFAULT THEME HERE');
[...]
variable_set('admin_theme', 'ADMIN THEME HERE');
}
This hook is probably not the right place to switch themes as it is invoked too late for this.
global $custom_theme
function MODULENAME_node_update($node) {
global $custom_theme;
$custom_theme = 'DEFAULT THEME HERE';
[...]
$custom_theme = 'ADMIN THEME HERE';
}
custom menu item
function MODULE_NAME_menu(){
$items = array();
$items['outputview'] = array(
'title' => 'Test',
'type' => MENU_CALLBACK,
'page callback' => 'MODULE_NAME_output_view',
'access callback' => TRUE,
'theme callback' => 'DEFAULT THEME HERE'
);
return $items;
}
function MODULE_NAME_output_view() {
$view = views_get_view('references');
$view->set_display('block');
$output = $view->render();
file_put_contents('references.html', $output);
}
function MODULE_NAME_node_update($node) {
unset($node->is_new);
unset($node->original);
entity_get_controller('node')->resetCache(array($node->nid));
menu_execute_active_handler('outputview', FALSE); // or via curl
}
This works as the view gets rendered correctly but still uses the admin theme.
hook_custom_theme
function MODULENAME_custom_theme(){
return 'DEFAULT THEME HERE';
}
I am looking for something similar. I found some code doing this (see patch #3 https://drupal.org/node/1813350), but it did not help with our implementation of the Shortcode contrib module. Hopefully it works for you or help you in the right direction.
This is our attempt derived from the patch:
$custom_theme_bu = drupal_static('menu_get_custom_theme');
$custom_theme = &drupal_static('menu_get_custom_theme');
$custom_theme = variable_get('theme_default', 'bartik');
unset($GLOBALS['theme']);
drupal_theme_initialize();
$embed_view = views_embed_view('YOUR_VIEW_ID', 'YOUR_VIEW_DISPLAY_ID');
$custom_theme = $custom_theme_bu;
unset($GLOBALS['theme']);
drupal_theme_initialize();
Here is some self-documenting code based on the answer by lmeurs:
/**
* Switch to or from an alternative theme in the middle of a request.
*
* This is useful if you need to render something (like a node) in a different
* theme without changing the theme of the entire page. An example use case is
* when you need to render something for a front end user from an admin page.
*
* Usage example:
* my_module_switch_theme('bartik');
* $node = node_load(1);
* $renderable = node_view($node);
* $rendered = render($renderable);
* my_module_switch_theme();
*
* #param string|null $to
* The name of the theme to switch to. If NULL, it switches back to the
* original theme.
*/
function my_module_switch_theme(string $to = NULL) {
// Backup the original theme.
static $original_theme;
if (empty($original_theme)) {
$original_theme = drupal_static('menu_get_custom_theme');
}
// Get a reference to the current theme value.
$custom_theme = &drupal_static('menu_get_custom_theme');
// Perform the switch.
$custom_theme = $to ?? $original_theme;
unset($GLOBALS['theme']);
drupal_theme_initialize();
}
Is there a way I can style the WordPress failure notice page? Take a look at the screenshot below which shows an example of the page. This page is generated at various times and for various reasons. To generate the page, open 2 tabs in your browser and login to the same WordPress site on each tab.
1: log out of the site via the first tab you have open.
2: log out via the second tab.
You'll see the failure page.
The approach I took was to set up a custom die handler which gives you the option to style all 'die' messages.
// custom die handler
function get_custom_die_handler() {
return 'custom_die_handler';
}
// hook the function
add_filter('wp_die_handler', 'get_custom_die_handler' );
// build a custom die handler
function custom_die_handler( $message, $title = '', $args = array() ) {
// whatever you want the die handler to do
}
Navigate to the file /wp-includes/functions.php look for a function called
wp_nonce_ays
That is the function which outputs the error page. That is a core function, which is called by the action of check_admin_referer. You could try to hook into that action; the only problem is that it dies after wp_nonce_ays is called, so using add_action has no effect since it dies before it would trigger. However, luckily, check_admin_referer is a pluggable function, so you can make a function to override it. Our function will be an exact copy of check_admin_referer except one extra line of code is added to style it. Save the function, I called it styleFailPage.php, and put it in your /wp-content/plugins/ folder.
<?php /*
* Plugin Name:Failure Styler
* Description:Adds a style element right before the failure notice is called
*/
if ( ! function_exists( 'wp_logout' ) ) {
function check_admin_referer($action = -1, $query_arg = '_wpnonce') {
if ( -1 == $action )
_doing_it_wrong( __FUNCTION__, __( 'You should specify a nonce action to be verified by using the first parameter.' ), '3.2' );
$adminurl = strtolower(admin_url());
$referer = strtolower(wp_get_referer());
$result = isset($_REQUEST[$query_arg]) ? wp_verify_nonce($_REQUEST[$query_arg], $action) : false;
if ( !$result && !(-1 == $action && strpos($referer, $adminurl) === 0) ) {
//this is the line I added:
echo "<style>body {background:red !important;}</style>";
wp_nonce_ays($action);
die();
}
do_action('check_admin_referer', $action, $result);
return $result;
}
}
?>
This will make invalid HTML because you have to end up inserting the <style> info ABOVE the doctype declaration, however AFAIK this is unavoidable without explicitly editing the core wp_nonce_ays function
Does anyone know how or can guide me in the right direction on how to add a body css class for the current node's taxonomy term? i.e. <body class="term-dogs"> where "dogs" is the taxonomy term name. It could also be just the term ID. Either way is fine I just need a solution. This will be for a Drupal 7 zen sub-theme
This answer took longer than I expected to figure out. The hard part was collecting the terms on the node, since All taxonomy functions relating to nodes have been removed or refactored. Ultimately, page 355 of Pro Drupal 7 Development saved the day with a snippet that does the job previously handled by taxonomy_node_get_terms.
Below is the code that worked for me (look for the part that says "MAGIC BEGINS HERE"). Assuming you're creating a sub-theme of Zen, you'll want to move this to your sub-theme's template.php file and rename it to YOURSUBTHEMENAME_preprocess_html:
/**
* Override or insert variables into the html template.
*
* #param $vars
* An array of variables to pass to the theme template.
* #param $hook
* The name of the template being rendered ("html" in this case.)
*/
function zen_preprocess_html(&$vars, $hook) {
// If the user is silly and enables Zen as the theme, add some styles.
if ($GLOBALS['theme'] == 'zen') {
include_once './' . drupal_get_path('theme', 'zen') . '/zen-internals/template.zen.inc';
_zen_preprocess_html($vars, $hook);
}
// Classes for body element. Allows advanced theming based on context
// (home page, node of certain type, etc.)
if (!$vars['is_front']) {
// Add unique class for each page.
$path = drupal_get_path_alias($_GET['q']);
// Add unique class for each website section.
list($section, ) = explode('/', $path, 2);
if (arg(0) == 'node') {
if (arg(1) == 'add') {
$section = 'node-add';
}
elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
$section = 'node-' . arg(2);
}
// MAGIC BEGINS HERE
$node = node_load(arg(1));
$results = field_view_field('node', $node, 'field_tags', array('default'));
foreach ($results as $key => $result) {
if (is_numeric($key)) {
$vars['classes_array'][] = strtolower($result['#title']);
}
}
// MAGIC ENDS HERE
}
$vars['classes_array'][] = drupal_html_class('section-' . $section);
}
if (theme_get_setting('zen_wireframes')) {
$vars['classes_array'][] = 'with-wireframes'; // Optionally add the wireframes style.
}
// Store the menu item since it has some useful information.
$vars['menu_item'] = menu_get_item();
switch ($vars['menu_item']['page_callback']) {
case 'views_page':
// Is this a Views page?
$vars['classes_array'][] = 'page-views';
break;
case 'page_manager_page_execute':
case 'page_manager_node_view':
case 'page_manager_contact_site':
// Is this a Panels page?
$vars['classes_array'][] = 'page-panels';
break;
}
}
I needed to know how to do this and Matt V's solution worked perfectly. I made a couple of additions to his work. I called drupal_html_class which replaces spaces and invalid characters. And I added in the term ID to allow you to target a term even if the name of the term changes.
// MAGIC BEGINS HERE
$node = node_load(arg(1));
$results = field_view_field('node', $node, 'field_tags', array('default'));
foreach ($results as $key => $result) {
if (is_numeric($key)) {
// Call drupal_html_class to make safe for a css class (remove spaces, invalid characters)
$vars['classes_array'][] = "taxonomy-" . strtolower(drupal_html_class( $result['#title']) );
// Add taxonomy ID. This will allow targeting of the taxonomy class even if the title changes
$vars['classes_array'][] = "taxonomy-id-" . $result['#options']['entity']->tid ;
}
}
// MAGIC ENDS HERE
Not sure what you mean with that body tag, but the classes on the node are generated here:
http://api.drupal.org/api/drupal/modules--node--node.module/function/template_preprocess_node/7
You can add more by implementing yourmodule_preprocess_node($vars) and then add whatever you want to $vars['classes_array']
Similar to the question posting form on SO, Drupal adds a draggable expander to the bottom of textareas created through the form api. How can I disable this in a nice manner?
The draggable expander gets added via the behavior defined in 'misc/textearea.js'. From that you can see that it applies to textareas having a class 'resizable'. You can prevent the output of this class if you set the '#resizable' property on a textareas FAPI definition to FALSE (it defaults to TRUE if not explicitly set).
So for your own forms, you can just declare the textareas accordingly. For other forms, youD need to adjust them via hook_form_alter().
A new module called Disable Resizable Textarea was release now.
This is a simple module that add ability to override the default #resizable property of textarea fields. By default, all textareas are resizable. This module allows you to disable this feature on each field.
It is very easy to set up. Just edit the desired field and you will see an "Disable #resizable property of this textarea" option. You can also disable resizable from its summary, if the field is of type "Long text with summary".
body textarea {
resize: none;
}
The simplest, would be to remove the file /misc/textarea.js.
The harder, but probably nicer way is to fix this in either your theme, or in a tiny module.
In your theme, you have two options again:
use a preprocess to remove the textarea.js from the list of javascript files.
use a theme override (yourtheme_textarea) to remove the class resizable-textarea from the rendered HTML. Some information on that in the forums
The option in a module, would be to run a hook_form_alter() to grab any form and run that trough a processor:
/**
* Implementation of hook_form_alter().
*
* Before Drupal 7, there is no way to easily identify form fields that are
* input format enabled. As a workaround, we assign a form #after_build
* processing callback that is executed on all forms after they have been
* completely built, so form elements are in their effective order
* and position already.
*
* #see wysiwyg_process_form()
*/ /**
* Implementation of hook_form_alter().
*
* Before Drupal 7, there is no way to easily identify form fields that are
* input format enabled. As a workaround, we assign a form #after_build
* processing callback that is executed on all forms after they have been
* completely built, so form elements are in their effective order
* and position already.
*
* #see wysiwyg_process_form()
*/
function wysiwyg_form_alter(&$form, &$form_state) {
$form['#after_build'][] = 'wysiwyg_process_form';
// Teaser splitter is unconditionally removed and NOT supported.
if (isset($form['body_field'])) {
unset($form['body_field']['teaser_js']);
}
}
function wysiwyg_process_form(&$form) {
// Iterate over element children; resetting array keys to access last index.
if ($children = array_values(element_children($form))) {
foreach ($children as $index => $item) {
$element = &$form[$item];
// filter_form() always uses the key 'format'. We need a type-agnostic
// match to prevent false positives. Also, there must have been at least
// one element on this level.
if (($item === 'format' || $item === 'signature_format') && $index > 0) {
// Make sure we either match a input format selector or input format
// guidelines (displayed if user has access to one input format only).
if ((isset($element['#type']) && $element['#type'] == 'fieldset') || isset($element['format']['guidelines'])) {
// The element before this element is the target form field.
$field = &$form[$children[$index - 1]];
$extra_class = '';
if (!empty($field['#resizable'])) {
$extra_class = ' wysiwyg-resizable-1';
drupal_add_js('misc/textarea.js');
}
// If we loaded at least one editor, then the 'none' editor will
// handle resizable textareas instead of core.
if (isset($loaded) && !empty($field['#resizable'])) {
$field['#resizable'] = FALSE;
}
}
// If this element is 'format', do not recurse further.
continue;
}
// Recurse into children.
wysiwyg_process_form($element);
}
}
return $form;
}
These examples are from WYSIWYG module, and slightly altered.
In your theme is far simple, but requires a theme that you can override. The module is both performance-wise worse, and a lot more complex. However, it will work on any theme.
There are to ways how to remove resizable textarea in Drupal (7).
1th Place this simple snipped into your theme template.php. Don't forget to rename THEMENAME to your theme name.
/**
* Override of theme('textarea').
* Deprecate misc/textarea.js in favor of using the 'resize' CSS3 property.
*/
function THEMENAME_textarea($variables) {
$element = $variables ['element'];
element_set_attributes($element, array('id', 'name', 'cols', 'rows'));
_form_set_class($element, array('form-textarea'));
$wrapper_attributes = array(
'class' => array('form-textarea-wrapper'),
);
$output = '<div' . drupal_attributes($wrapper_attributes) . '>';
$output .= '<textarea' . drupal_attributes($element ['#attributes']) . '>' . check_plain($element ['#value']) . '</textarea>';
$output .= '</div>';
return $output;
}
2nd Other way is to use another module called Disable resizable textarea.
More info and source.