security update core+modules

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-26 18:38:56 +02:00
parent 2f45ea820a
commit 7c96373038
1022 changed files with 30319 additions and 11259 deletions

View File

@ -49,6 +49,34 @@ function ctools_ajax_text_button($text, $dest, $alt, $class = '', $type = 'use-a
return l($text, $dest, array('html' => TRUE, 'attributes' => array('class' => array($type, $class), 'title' => $alt)));
}
/**
* Render an icon and related text as a link. This will automatically apply an AJAX class
* to the link and add the appropriate javascript to make this happen.
*
* Note: 'html' => true so be sure any text is vetted! Chances are these kinds of buttons will
* not use user input so this is a very minor concern.
*
* @param $text
* The text that will be displayed as the link.
* @param $image
* The icon image to include in the link.
* @param $dest
* The destination of the link.
* @param $alt
* The title text of the link.
* @param $class
* Any class to apply to the link. @todo this should be a options array.
* @param $type
* A type to use, in case a different behavior should be attached. Defaults
* to ctools-use-ajax.
*/
function ctools_ajax_icon_text_button($text, $image, $dest, $alt, $class = '', $type = 'use-ajax') {
drupal_add_library('system', 'drupal.ajax');
$rendered_image = theme('image', array('path' => $image));
$link_content = $rendered_image . "<span>" . $text . "</span>";
return l($link_content, $dest, array('html' => TRUE, 'attributes' => array('class' => array($type, $class), 'title' => $alt)));
}
/**
* Set a single property to a value, on all matched elements.
*

View File

@ -244,6 +244,7 @@ function ctools_content_prepare_subtype(&$subtype, $plugin) {
* The content as rendered by the plugin. This content should be an array
* with the following possible keys:
* - title: The safe to render title of the content.
* - title_heading: The title heading.
* - content: The safe to render HTML content.
* - links: An array of links associated with the content suitable for
* theme('links').
@ -299,6 +300,7 @@ function ctools_content_render($type, $subtype, $conf, $keywords = array(), $arg
$keywords['%title'] = empty($content->title) ? '' : $content->title;
$content->original_title = $keywords['%title'];
$content->title = $conf['override_title_text'];
$content->title_heading = isset($conf['override_title_heading']) ? $conf['override_title_heading'] : 'h2';
}
if (!empty($content->title)) {
@ -504,8 +506,26 @@ function ctools_content_configure_form_defaults($form, &$form_state) {
'#size' => 35,
'#id' => 'override-title-textfield',
'#dependency' => array('override-title-checkbox' => array(1)),
'#dependency_type' => 'disable',
'#dependency_type' => 'hidden',
);
$form['override_title_heading'] = array(
'#type' => 'select',
'#default_value' => isset($conf['override_title_heading']) ? $conf['override_title_heading'] : 'h2',
'#options' => array(
'h1' => t('h1'),
'h2' => t('h2'),
'h3' => t('h3'),
'h4' => t('h4'),
'h5' => t('h5'),
'h6' => t('h6'),
'div' => t('div'),
'span' => t('span'),
),
'#id' => 'override-title-heading',
'#dependency' => array('override-title-checkbox' => array(1)),
'#dependency_type' => 'hidden',
);
$form['aligner_stop'] = array(
'#markup' => '</div>',
);
@ -536,6 +556,7 @@ function ctools_content_configure_form_defaults_submit(&$form, &$form_state) {
if (isset($form_state['values']['override_title'])) {
$form_state['conf']['override_title'] = $form_state['values']['override_title'];
$form_state['conf']['override_title_text'] = $form_state['values']['override_title_text'];
$form_state['conf']['override_title_heading'] = $form_state['values']['override_title_heading'];
}
}
@ -687,6 +708,11 @@ function ctools_content_get_available_types($contexts = NULL, $has_content = FAL
}
}
// Check if the content type provides an access callback.
if (isset($subtype['create content access']) && function_exists($subtype['create content access']) && !$subtype['create content access']($plugin, $subtype)) {
continue;
}
// If we made it through all the tests, then we can use this content.
$available[$id][$subtype_id] = $subtype;
}

View File

@ -23,53 +23,157 @@ function ctools_content_menu(&$items) {
/**
* Helper function for autocompletion of entity titles.
*/
function ctools_content_autocomplete_entity($type, $string = '') {
$entity = entity_get_info($type);
function ctools_content_autocomplete_entity($entity_type, $string = '') {
if ($string != '') {
// @todo verify the query logic here, it's untested.
// Set up the query
$query = db_select($entity['base table'], 'b');
if ($entity['entity keys']['label']) {
$query->fields('b', array($entity['entity keys']['id'], $entity['entity keys']['label']))->range(0, 10);
}
else {
$query->fields('b', array($entity['entity keys']['id']))->range(0, 10);
$entity_info = entity_get_info($entity_type);
if (!module_exists('entity')) {
module_load_include('inc', 'ctools', 'includes/entity-access');
_ctools_entity_access($entity_info, $entity_type);
}
// We must query all ids, because if every one of the 10 don't have access
// the user may never be able to autocomplete a node title.
$preg_matches = array();
$matches = array();
$match = preg_match('/\[id: (\d+)\]/', $string, $preg_matches);
if (!$match) {
$match = preg_match('/^id: (\d+)/', $string, $preg_matches);
}
// If an ID match was found, use that ID rather than the whole string.
if ($match) {
$query->condition('b.' . $entity['entity keys']['id'], $preg_matches[1]);
}
elseif ($entity['entity keys']['label']) {
$query->condition('b.' . $entity['entity keys']['label'], '%' . db_like($string) . '%', 'LIKE');
}
$matches = array();
if ($type == 'node') {
$query->addTag('node_access');
$query->join('users', 'u', 'b.uid = u.uid');
$query->addField('u', 'name', 'name');
foreach ($query->execute() as $nodeish) {
$name = empty($nodeish->name) ? variable_get('anonymous', t('Anonymous')) : check_plain($nodeish->name);
$matches[$nodeish->title . " [id: $nodeish->nid]"] = '<span class="autocomplete_title">' . check_plain($nodeish->title) . '</span> <span class="autocomplete_user">(' . t('by @user', array('@user' => $name)) . ')</span>';
}
$entity_id = $preg_matches[1];
$results = _ctools_getReferencableEntities($entity_type, $entity_info, $entity_id, '=', 1);
}
else {
foreach ($query->execute() as $item) {
$id = $item->{$entity['entity keys']['id']};
if ($entity['entity keys']['label']) {
$matches[$item->{$entity['entity keys']['label']} . " [id: $id]"] = '<span class="autocomplete_title">' . check_plain($item->{$entity['entity keys']['label']}) . '</span>';
}
else {
$matches["[id: $id]"] = '<span class="autocomplete_title">' . check_plain($item->{$entity['entity keys']['id']}) . '</span>';
}
// We cannot find results if the entity doesn't have a label to search.
if (!isset($entity_info['entity keys']['label'])) {
drupal_json_output(array("[id: NULL]" => '<span class="autocomplete_title">' . t('Entity Type !entity_type does not support autocomplete search.', array('!entity_type' => $entity_type)) . '</span>'));
return;
}
$results = _ctools_getReferencableEntities($entity_type, $entity_info, $string, 'LIKE', 10);
}
foreach ($results as $entity_id => $result) {
$matches[$result['label'] . " [id: $entity_id]"] = '<span class="autocomplete_title">' . check_plain($result['label']) . '</span>';
$matches[$result['label'] . " [id: $entity_id]"] .= isset($result['bundle']) ? ' <span class="autocomplete_bundle">(' . check_plain($result['bundle']) . ')</span>' : '';
}
drupal_json_output($matches);
}
}
/*
* Use well known/tested entity reference code to build our search query
* From EntityReference_SelectionHandler_Generic class
*/
function _ctools_buildQuery($entity_type, $entity_info, $match = NULL, $match_operator = 'CONTAINS') {
$base_table = $entity_info['base table'];
$label_key = $entity_info['entity keys']['label'];
$query = db_select($base_table)
->fields($base_table, array($entity_info['entity keys']['id']));
if (isset($match)) {
if (isset($label_key)) {
$query->condition($base_table . '.' . $label_key, '%' . $match . '%', $match_operator);
}
// This should never happen, but double check just in case.
else {
return array();
}
}
// Add a generic entity access tag to the query.
$query->addTag('ctools');
// We have to perform two checks. First check is a query alter (with tags)
// in an attempt to only return results that have access. However, this is
// not full-proof since entities many not implement hook_access query tag.
// This is why we have a second check after entity load, before we display
// the label of an entity.
if ($entity_type == 'comment') {
// Adding the 'comment_access' tag is sadly insufficient for comments: core
// requires us to also know about the concept of 'published' and
// 'unpublished'.
if (!user_access('administer comments')) {
$query->condition('comment.status', COMMENT_PUBLISHED);
}
// Join to a node if the user does not have node access bypass permissions
// to obey node published permissions
if (!user_access('bypass node access')) {
$node_alias = $query->innerJoin('node', 'n', '%alias.nid = comment.nid');
$query->condition($node_alias . '.status', NODE_PUBLISHED);
}
$query->addTag('node_access');
}
else {
$query->addTag($entity_type . '_access');
}
// Add the sort option.
if (isset($label_key)) {
$query->orderBy($base_table . '.' . $label_key, 'ASC');
}
return $query;
}
/**
* Private function to get referencable entities. Based on code from the
* Entity Reference module.
*/
function _ctools_getReferencableEntities($entity_type, $entity_info, $match = NULL, $match_operator = 'LIKE', $limit = 0) {
global $user;
$account = $user;
$options = array();
// We're an entity ID, return the id
if (is_numeric($match) && $match_operator == '=') {
if ($entity = array_shift(entity_load($entity_type, array($match)))) {
if (isset($entity_info['access callback']) && function_exists($entity_info['access callback'])) {
if ($entity_info['access callback']('view', $entity, $account, $entity_type)) {
$label = entity_label($entity_type, $entity);
return array(
$match => array(
'label' => !empty($label) ? $label : $entity->{$entity_info['entity keys']['id']},
'bundle' => !empty($entity_info['entity keys']['bundle']) ? check_plain($entity->{$entity_info['entity keys']['bundle']}) : NULL,
),
);
}
}
}
// If you don't have access, or an access callback or a valid entity, just
// Return back the Entity ID.
return array(
$match => array(
'label' => $match,
'bundle' => NULL,
),
);
}
// We have matches, build a query to fetch the result.
if ($query = _ctools_buildQuery($entity_type, $entity_info, $match, $match_operator)) {
if ($limit > 0) {
$query->range(0, $limit);
}
$results = $query->execute();
if (!empty($results)) {
foreach ($results as $record) {
$entities = entity_load($entity_type, array($record->{$entity_info['entity keys']['id']}));
$entity = array_shift($entities);
if (isset($entity_info['access callback']) && function_exists($entity_info['access callback'])) {
if ($entity_info['access callback']('view', $entity, $account, $entity_type)) {
$label = entity_label($entity_type, $entity);
$options[$record->{$entity_info['entity keys']['id']}] = array(
'label' => !empty($label) ? $label : $entity->{$entity_info['entity keys']['id']},
'bundle' => !empty($entity_info['entity keys']['bundle']) ? check_plain($entity->{$entity_info['entity keys']['bundle']}) : NULL,
);
}
}
}
}
return $options;
}
return array();
}

View File

@ -10,7 +10,7 @@ function ctools_content_plugin_type(&$items) {
'cache' => FALSE,
'process' => array(
'function' => 'ctools_content_process',
'file' => 'export-ui.inc',
'file' => 'content.inc',
'path' => drupal_get_path('module', 'ctools') . '/includes',
),
);

View File

@ -376,11 +376,11 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke
'path' => "ctools/context/ajax/add/$mechanism/$type/$cache_key/$name/%step",
'show cancel' => TRUE,
'default form' => 'ctools_edit_context_form_defaults',
'auto caching' => TRUE,
'auto cache' => TRUE,
'cache mechanism' => $mechanism,
'cache key' => $cache_key,
// This is stating what the cache will be referred to in $form_state
'cache storage' => 'object',
'cache location' => 'object',
);
if ($type == 'requiredcontext') {
@ -416,12 +416,20 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke
ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
// Very irritating way to update the form for our contexts.
$arg_form_state = array('values' => array());
$arg_form_state = form_state_defaults() + array(
'values' => array(),
'process_input' => FALSE,
'complete form' => array(),
);
$rel_form_state = $arg_form_state;
$arg_form = array(
'#post' => array(),
'#programmed' => FALSE,
'#tree' => FALSE,
'#parents' => array(),
'#array_parents' => array(),
);
// Build a chunk of the form to merge into the displayed form
@ -446,6 +454,8 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke
'#post' => array(),
'#programmed' => FALSE,
'#tree' => FALSE,
'#parents' => array(),
'#array_parents' => array(),
);
$rel_form['relationship'] = array(
@ -460,7 +470,7 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke
$output = array();
if (!empty($available_relationships)) {
ctools_context_add_item_table_buttons('relationship', $mechanism, $rel_form, $available_relationships);
$rel_form = form_builder('dummy_form_id', $rel_form, $arg_form_state);
$rel_form = form_builder('dummy_form_id', $rel_form, $rel_form_state);
$output[] = ajax_command_replace('div#ctools-relationships-table div.buttons', drupal_render($rel_form));
}
@ -551,11 +561,11 @@ function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_k
'path' => "ctools/context/ajax/configure/$mechanism/$type/$cache_key/$position/%step",
'show cancel' => TRUE,
'default form' => 'ctools_edit_context_form_defaults',
'auto caching' => TRUE,
'auto cache' => TRUE,
'cache mechanism' => $mechanism,
'cache key' => $cache_key,
// This is stating what the cache will be referred to in $form_state
'cache storage' => 'object',
'cache location' => 'object',
);
if ($type == 'requiredcontext') {
@ -582,8 +592,16 @@ function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_k
$output = array();
$output[] = ctools_modal_command_dismiss();
$arg_form_state = form_state_defaults() + array(
'values' => array(),
'process_input' => FALSE,
'complete form' => array(),
);
$arg_form = array(
'#post' => array(),
'#parents' => array(),
'#array_parents' => array(),
'#programmed' => FALSE,
'#tree' => FALSE,
);

View File

@ -143,6 +143,10 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte
return MENU_ACCESS_DENIED;
case 404:
return MENU_NOT_FOUND;
case 410:
drupal_add_http_header('Status', '410 Gone');
drupal_exit();
break;
case 301:
case 302:
case 303:
@ -157,7 +161,7 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte
'query' => $info['query'],
'fragment' => $info['fragment'],
);
return drupal_goto($info['destination'], $options, $info['response code']);
drupal_goto($info['destination'], $options, $info['response code']);
// @todo -- should other response codes be supported here?
}
}

View File

@ -169,7 +169,12 @@ class ctools_context_required {
function select($contexts, $context) {
if (!is_array($contexts)) {
$contexts = array($contexts);
if (is_object($contexts) && $contexts instanceof ctools_context) {
$contexts = array($contexts->id => $contexts);
}
else {
$contexts = array($contexts);
}
}
// If we had requested a $context but that $context doesn't exist
@ -328,13 +333,15 @@ function _ctools_context_selector($contexts, $required, $default, $num = 0) {
$title = $num ? t('Context %count', array('%count' => $num)) : t('Context');
}
return array(
$form = array(
'#type' => 'select',
'#options' => $options,
'#title' => $title,
'#default_value' => $default,
);
}
return $form;
}
/**
@ -1400,8 +1407,7 @@ function ctools_get_relevant_access_plugins($contexts) {
* Create a context for the logged in user.
*/
function ctools_access_get_loggedin_context() {
global $user;
$context = ctools_context_create('entity:user', $user);
$context = ctools_context_create('entity:user', array('type' => 'current'), TRUE);
$context->identifier = t('Logged in user');
$context->keyword = 'viewer';
$context->id = 0;
@ -1588,7 +1594,9 @@ function ctools_access_add_restrictions($settings, $contexts) {
$required_context = isset($plugin['required context']) ? $plugin['required context'] : array();
$context = isset($test['context']) ? $test['context'] : array();
$contexts = ctools_context_select($contexts, $required_context, $context);
$function($test['settings'], $contexts);
if ($contexts !== FALSE) {
$function($test['settings'], $contexts);
}
}
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* @file
* Custom cache implementation for the CTools CSS cache.
*/
class CToolsCssCache implements DrupalCacheInterface {
/**
* {@inheritdoc}
*/
public function clear($cid = NULL, $wildcard = FALSE) {
// Only clear the caches if the wildcard is set, this ensures that the cache
// is only cleared when the full caches are cleared manually (eg by invoking
// drupal_flush_all_caches()), and not on a cron run.
// @see drupal_flush_all_caches()
// @see system_cron()
if ($wildcard) {
ctools_include('css');
ctools_css_flush_caches();
}
}
/**
* {@inheritdoc}
*/
public function get($cid) {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getMultiple(&$cids) {
return array();
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function set($cid, $data, $expire = CACHE_PERMANENT) {
}
}

View File

@ -77,9 +77,9 @@ function ctools_css_store($id, $css, $filter = TRUE) {
$filename = ctools_css_cache($css, $filter);
db_insert('ctools_css_cache')
db_merge('ctools_css_cache')
->key(array('cid' => $id))
->fields(array(
'cid' => $id,
'filename' => $filename,
'css' => $css,
'filter' => intval($filter),

View File

@ -4,6 +4,8 @@
* @file
* Provide a javascript based dropbutton menu.
*
* An example are the edit/disable/delete links on the views listing page.
*
* The dropbutton menu will show up as a button with a clickable twisty pointer
* to the right. When clicked the button will expand, showing the list of links.
*

View File

@ -4,6 +4,9 @@
* @file
* Provide a javascript based dropdown menu.
*
* An example are the dropdown settings in the panels ui, like for adding
* new panes.
*
* The dropdown menu will show up as a clickable link; when clicked,
* a small menu will slide down beneath it, showing the list of links.
*

View File

@ -0,0 +1,150 @@
<?php
/**
* @file
* Provides various callbacks for the whole core module integration.
* This is a copy of Entity API's functionality for use when Entity API isn't
* Enabled, and only works on view functions.
*/
/**
* Core hack to include entity api-esque 'access callback' functions to core
* entities without needing to rely on entity api.
* Exception: We don't touch file entity. You must have entity API enabled to
* view files.
*/
function _ctools_entity_access(&$entity_info, $entity_type) {
// If the access callback is already set, don't change anything.
if (isset($entity_info['access callback'])) {
return;
}
switch ($entity_type) {
case 'node':
// Sad panda, we don't use Entity API, lets manually add access callbacks.
$entity_info['access callback'] = 'ctools_metadata_no_hook_node_access';
break;
case 'user':
$entity_info['access callback'] = 'ctools_metadata_user_access';
break;
case 'comment':
if (module_exists('comment')) {
$entity_info['access callback'] = 'ctools_metadata_comment_access';
}
break;
case 'taxonomy_term':
if (module_exists('taxonomy')) {
$entity_info['access callback'] = 'ctools_metadata_taxonomy_access';
}
break;
case 'taxonomy_vocabulary':
if (module_exists('taxonomy')) {
$entity_info['access callback'] = 'ctools_metadata_taxonomy_access';
}
break;
}
}
/**
* Access callback for the node entity.
*
* This function does not implement hook_node_access(), thus it may not be
* called ctools_metadata_node_access().
*
* @see entity_access()
*
* @param $op
* The operation being performed. One of 'view', 'update', 'create' or
* 'delete'.
* @param $node
* A node to check access for. Must be a node object. Must have nid,
* except in the case of 'create' operations.
* @param $account
* The user to check for. Leave it to NULL to check for the global user.
*
* @throws EntityMalformedException
*
* @return boolean
* TRUE if access is allowed, FALSE otherwise.
*/
function ctools_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) {
// First deal with the case where a $node is provided.
if (isset($node)) {
// If a non-default revision is given, incorporate revision access.
$default_revision = node_load($node->nid);
if ($node->vid !== $default_revision->vid) {
return _node_revision_access($node, $op, $account);
}
else {
return node_access($op, $node, $account);
}
}
// No node is provided. Check for access to all nodes.
if (user_access('bypass node access', $account)) {
return TRUE;
}
if (!user_access('access content', $account)) {
return FALSE;
}
if ($op == 'view' && node_access_view_all_nodes($account)) {
return TRUE;
}
return FALSE;
}
/**
* Access callback for the user entity.
*/
function ctools_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type) {
$account = isset($account) ? $account : $GLOBALS['user'];
// Grant access to the users own user account and to the anonymous one.
if (isset($entity) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) {
return TRUE;
}
if (user_access('administer users', $account) || user_access('access user profiles', $account) && $op == 'view' && $entity->status) {
return TRUE;
}
return FALSE;
}
/**
* Access callback for the comment entity.
*/
function ctools_metadata_comment_access($op, $entity = NULL, $account = NULL) {
// When determining access to a comment, if comment has an associated node,
// the user must be able to view the node in order to access the comment.
if (isset($entity->nid)) {
if (!node_access('view', node_load($entity->nid), $account)) {
return FALSE;
}
}
// Comment administrators are allowed to perform all operations on all
// comments.
if (user_access('administer comments', $account)) {
return TRUE;
}
// Unpublished comments can never be accessed by non-admins.
if (isset($entity->status) && $entity->status == COMMENT_NOT_PUBLISHED) {
return FALSE;
}
if (user_access('access comments', $account) && $op == 'view') {
return TRUE;
}
return FALSE;
}
/**
* Access callback for the taxonomy entities.
*/
function ctools_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type) {
if ($entity_type == 'taxonomy_vocabulary') {
return user_access('administer taxonomy', $account);
}
if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') {
return TRUE;
}
return FALSE;
}

View File

@ -945,7 +945,13 @@ function ctools_export_object($table, $object, $indent = '', $identifier = NULL,
else {
$value = $object->$field;
if ($info['type'] == 'int') {
$value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value;
if (isset($info['size']) && $info['size'] == 'tiny') {
$info['boolean'] = (!isset($info['boolean'])) ? $schema['export']['boolean'] : $info['boolean'];
$value = ($info['boolean']) ? (bool) $value : (int) $value;
}
else {
$value = (int) $value;
}
}
$output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n";
@ -967,7 +973,12 @@ function ctools_export_object($table, $object, $indent = '', $identifier = NULL,
* that it's easily available.
*/
function ctools_export_get_schema($table) {
$cache = &drupal_static(__FUNCTION__);
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
}
$cache = &$drupal_static_fast['cache'];
if (empty($cache[$table])) {
$schema = drupal_get_schema($table);
@ -1003,6 +1014,7 @@ function ctools_export_get_schema($table) {
'cache defaults' => FALSE,
'default cache bin' => 'cache',
'export type string' => 'type',
'boolean' => TRUE,
);
// If the export definition doesn't have the "primary key" then the CRUD

View File

@ -79,6 +79,19 @@ function ctools_fields_get_field_formatter_settings_form($field, $formatter_type
$instance = ctools_fields_fake_field_instance($field['field_name'], $view_mode, $formatter_type, $conf['formatter_settings']);
$settings_form = $function($field, $instance, $view_mode, $form, $form_state);
if ($settings_form) {
// Allow other modules to alter the formatter settings form.
$context = array(
'module' => $formatter['module'],
'formatter' => $formatter,
'field' => $field,
'instance' => $instance,
'view_mode' => $view_mode,
'form' => $form,
'form_state' => $form_state,
);
drupal_alter('field_formatter_settings_form', $settings_form, $context);
$settings_form['#tree'] = TRUE;
$form['ctools_field_list']['#value'][] = $field;
$form += $settings_form;
}

View File

@ -187,18 +187,32 @@ function ctools_modal_text_button($text, $dest, $alt, $class = '') {
/**
* Wrap a form so that we can use it properly with AJAX. Essentially if the
* form wishes to render, it automatically does that, otherwise it returns
* so we can see submission results.
* the render array so we can see submission results.
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
* If the 'reset_html_ids' key is set to TRUE, it will prevent HTML IDs in
* forms from being incremented.
*
* @return
* @return mixed
* The output of the form, if it was rendered. If $form_state['ajax']
* is set, this will use ctools_modal_form_render so it will be
* a $command object suitable for ajax_render already.
*
* The return will be NULL if the form was successfully submitted unless
* you specifically set re_render = TRUE. If ajax is set the
* form will never be redirected.
* If the form was not rendered, the raw render array will be returned.
*
* If ajax is set the form will never be redirected.
*/
function ctools_modal_form_wrapper($form_id, &$form_state) {
// Since this will run again on form rebuild while still in the modal, prevent
// form IDs from being incremented.
// @todo https://drupal.org/node/1305882
if (!empty($form_state['reset_html_ids']) && !empty($_POST['ajax_html_ids'])) {
unset($_POST['ajax_html_ids']);
}
// This won't override settings already in.
$form_state += array(
're_render' => FALSE,
@ -206,7 +220,7 @@ function ctools_modal_form_wrapper($form_id, &$form_state) {
);
$output = drupal_build_form($form_id, $form_state);
if (!empty($form_state['ajax']) && (!$form_state['executed'] || $form_state['rebuild'])) {
if (!empty($form_state['ajax']) && (!$form_state['executed'] || $form_state['rebuild'])) {
return ctools_modal_form_render($form_state, $output);
}

View File

@ -214,7 +214,12 @@ function ctools_plugin_api_get_hook($owner, $api) {
function ctools_get_plugins($module, $type, $id = NULL) {
// Store local caches of plugins and plugin info so we don't have to do full
// lookups everytime.
$plugins = &drupal_static('ctools_plugins', array());
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['plugins'] = &drupal_static('ctools_plugins', array());
}
$plugins = &$drupal_static_fast['plugins'];
$info = ctools_plugin_get_plugin_type_info();
// Bail out noisily if an invalid module/type combination is requested.
@ -343,8 +348,13 @@ function ctools_get_plugins($module, $type, $id = NULL) {
* name and each inner array keyed on plugin type name.
*/
function ctools_plugin_get_plugin_type_info($flush = FALSE) {
$info_loaded = &drupal_static('ctools_plugin_type_info_loaded', FALSE);
$all_type_info = &drupal_static('ctools_plugin_type_info', array());
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['info_loaded'] = &drupal_static('ctools_plugin_type_info_loaded', FALSE);
$drupal_static_fast['all_type_info'] = &drupal_static('ctools_plugin_type_info', array());
}
$info_loaded = &$drupal_static_fast['info_loaded'];
$all_type_info = &$drupal_static_fast['all_type_info'];
// Only trigger info loading once.
if ($info_loaded && !$flush) {
@ -418,29 +428,19 @@ function ctools_plugin_load_includes($info, $filename = NULL) {
// store static of plugin arrays for reference because they can't be reincluded.
static $plugin_arrays = array();
// If we're being asked for all plugins of a type, skip any caching
// we may have done because this is an admin task and it's ok to
// spend the extra time.
if (!isset($filename)) {
$all_files[$info['module']][$info['type']] = NULL;
}
if (!isset($all_files[$info['module']][$info['type']])) {
// If a filename was set, we will try to load our list of files from
// cache. This is considered normal operation and we try to reduce
// the time spent finding files.
if (isset($filename)) {
$cache = cache_get("ctools_plugin_files:$info[module]:$info[type]");
if ($cache) {
$all_files[$info['module']][$info['type']] = $cache->data;
}
$cache = cache_get("ctools_plugin_files:$info[module]:$info[type]");
if ($cache) {
$all_files[$info['module']][$info['type']] = $cache->data;
}
if (!isset($all_files[$info['module']][$info['type']])) {
// Do not attempt any file scan even if the cached entry was empty.
// A NULL entry here would mean the plugin just does not exists, and we
// cannot afford to run file scan on production sites normal run.
elseif (!isset($all_files[$info['module']][$info['type']])) {
$all_files[$info['module']][$info['type']] = array();
// Load all our plugins.
$directories = ctools_plugin_get_directories($info);
$extension = empty($info['info file']) ? $info['extension'] : 'info';
$extension = (empty($info['info file']) || ($info['extension'] != 'inc')) ? $info['extension'] : 'info';
foreach ($directories as $module => $path) {
$all_files[$info['module']][$info['type']][$module] = file_scan_directory($path, '/\.' . $extension . '$/', array('key' => 'name'));
@ -678,7 +678,7 @@ function ctools_plugin_process($info, $module, $identifier, $path, $file = NULL,
if (!function_exists($function)) {
return NULL;
}
$result = $function();
$result = $function($info);
if (!isset($result) || !is_array($result)) {
return NULL;
}

View File

@ -29,16 +29,3 @@ function ctools_passthrough($module, $type, &$items) {
}
}
}
/**
* Implementation of hook_theme_registry_alter()
*/
function ctools_theme_registry_alter(&$registry) {
// Move this one last last last so it can catch changes made by modules and themes.
$key = array_search('ctools_preprocess_page', $registry['page']['preprocess functions']);
if ($key) {
unset($registry['page']['preprocess functions'][$key]);
}
$registry['page']['preprocess functions'][] = 'ctools_preprocess_page';
}

View File

@ -0,0 +1,67 @@
<?php
/**
* @file
* Enables ctools generated modules to use UUIDs without the UUID module enabled.
* Per the ctools.module, this file only gets included if UUID doesn't exist.
*/
/**
* Pattern for detecting a valid UUID.
*/
define('UUID_PATTERN', '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}');
/**
* Generates a UUID using the Windows internal GUID generator.
*
* @see http://php.net/com_create_guid
*/
function _ctools_uuid_generate_com() {
// Remove {} wrapper and make lower case to keep result consistent.
return drupal_strtolower(trim(com_create_guid(), '{}'));
}
/**
* Generates an universally unique identifier using the PECL extension.
*/
function _ctools_uuid_generate_pecl() {
return uuid_create(UUID_TYPE_DEFAULT);
}
/**
* Generates a UUID v4 using PHP code.
*
* Based on code from @see http://php.net/uniqid#65879 , but corrected.
*/
function _ctools_uuid_generate_php() {
// The field names refer to RFC 4122 section 4.1.2.
return sprintf('%04x%04x-%04x-4%03x-%04x-%04x%04x%04x',
// 32 bits for "time_low".
mt_rand(0, 65535), mt_rand(0, 65535),
// 16 bits for "time_mid".
mt_rand(0, 65535),
// 12 bits after the 0100 of (version) 4 for "time_hi_and_version".
mt_rand(0, 4095),
bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '10', 0, 2)),
// 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
// (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
// 8 bits for "clk_seq_low" 48 bits for "node".
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)
);
}
// This is wrapped in an if block to avoid conflicts with PECL's uuid_is_valid().
/**
* Check that a string appears to be in the format of a UUID.
*
* @param $uuid
* The string to test.
*
* @return
* TRUE if the string is well formed.
*/
if (!function_exists('uuid_is_valid')) {
function uuid_is_valid($uuid) {
return preg_match('/^' . UUID_PATTERN . '$/', $uuid);
}
}

View File

@ -274,7 +274,7 @@ function ctools_wizard_wrapper($form, &$form_state) {
if (!empty($form_info['show trail'])) {
ctools_add_css('wizard');
$form['ctools_trail'] = array(
'#markup' => theme(array('ctools_wizard_trail__' . $form_info['id'], 'ctools_wizard_trail'), array('trail' => $trail)),
'#markup' => theme(array('ctools_wizard_trail__' . $form_info['id'], 'ctools_wizard_trail'), array('trail' => $trail, 'form_info' => $form_info)),
'#weight' => -1000,
);
}

View File

@ -7,19 +7,16 @@
function ctools_wizard_theme(&$theme) {
$theme['ctools_wizard_trail'] = array(
'variables' => array('trail' => NULL),
'variables' => array('trail' => NULL, 'form_info' => NULL, 'divider' => ' » '),
'file' => 'includes/wizard.theme.inc',
);
}
/**
* Themable display of the 'breadcrumb' trail to show the order of the
* forms.
* Themable display of the 'breadcrumb' trail to show the order of the forms.
*/
function theme_ctools_wizard_trail($vars) {
$trail = $vars['trail'];
if (!empty($trail)) {
return '<div class="wizard-trail">' . implode(' » ', $trail) . '</div>';
if (!empty($vars['trail'])) {
return '<div class="wizard-trail">' . implode($vars['divider'], $vars['trail']) . '</div>';
}
}