diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index da3a277..481adea 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1752,7 +1752,8 @@ function drupal_anonymous_user() { * * In order to bootstrap Drupal from another PHP script, you can use this code: * @code - * require_once '/path/to/drupal/core/includes/bootstrap.inc'; + * define('DRUPAL_ROOT', '/path/to/drupal'); + * require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc'; * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); * @endcode * diff --git a/core/includes/common.inc b/core/includes/common.inc index 1980f2f..358a59a 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2,7 +2,6 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Json; -use Drupal\Component\Utility\SortArray; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Tags; use Drupal\Component\Utility\Url; @@ -2835,9 +2834,7 @@ function drupal_get_library($module, $name = NULL) { if (is_scalar($options)) { // The JavaScript or CSS file has been specified in shorthand // format, without an array of options. In this case $options is the - // filename. Convert the shorthand version and remove the old array - // key. - unset($module_libraries[$key]['js'][$file]); + // filename. $file = $options; $options = array(); } @@ -3731,31 +3728,7 @@ function drupal_render_page($page) { * - If this element has an array of #theme_wrappers defined and * #render_children is not set, #children is then re-rendered by passing the * element in its current state to theme() successively for each item in - * #theme_wrappers. Since #theme and #theme_wrappers hooks often define - * variables with the same names it is possible to explicitly override each - * attribute passed to each #theme_wrappers hook by setting the hook name as - * the key and an array of overrides as the value in #theme_wrappers array. - * For example, if we have a render element as follows: - * @code - * array( - * '#theme' => 'image', - * '#attributes' => array('class' => 'foo'), - * '#theme_wrappers' => array('container'), - * ); - * @endcode - * and we need to pass the class 'bar' as an attribute for 'container', we - * can rewrite our element thus: - * @code - * array( - * '#theme' => 'image', - * '#attributes' => array('class' => 'foo'), - * '#theme_wrappers' => array( - * 'container' => array( - * '#attributes' => array('class' => 'bar'), - * ), - * ), - * ); - * @endcode + * #theme_wrappers. * - If this element has an array of #post_render functions defined, they are * called sequentially to modify the rendered #children. Unlike #pre_render * functions, #post_render functions are passed both the rendered #children @@ -3884,24 +3857,8 @@ function drupal_render(&$elements) { // If the internal #render_children property is set, do not call the // #theme_wrappers function(s) to prevent infinite recursion. if (isset($elements['#theme_wrappers']) && !isset($elements['#render_children'])) { - foreach ($elements['#theme_wrappers'] as $key => $value) { - // If the value of a #theme_wrappers item is an array then the theme hook - // is found in the key of the item and the value contains attribute - // overrides. Attribute overrides replace key/value pairs in $elements for - // only this theme() call. This allows #theme hooks and #theme_wrappers - // hooks to share variable names without conflict or ambiguity. - $wrapper_elements = $elements; - if (is_array($value)) { - $wrapper_hook = $key; - foreach ($value as $attribute => $override) { - $wrapper_elements[$attribute] = $override; - } - } - else { - $wrapper_hook = $value; - } - - $elements['#children'] = theme($wrapper_hook, $wrapper_elements); + foreach ($elements['#theme_wrappers'] as $theme_wrapper) { + $elements['#children'] = theme($theme_wrapper, $elements); } } @@ -4274,16 +4231,14 @@ function drupal_render_cid_create($elements) { * that optionally include a '#weight' key. * @param $b * Second item for comparison. - * - * @return int - * The comparison result for uasort(). - * - * @see \Drupal\Component\Utility\SortArray::sortByWeightProperty() - * - * @deprecated as of Drupal 8.0. Use SortArray::sortByWeightProperty() instead. */ function element_sort($a, $b) { - return SortArray::sortByWeightProperty($a, $b); + $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0; + $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0; + if ($a_weight == $b_weight) { + return 0; + } + return ($a_weight < $b_weight) ? -1 : 1; } /** @@ -4298,16 +4253,11 @@ function element_sort($a, $b) { * that optionally include a '#title' key. * @param $b * Second item for comparison. - * - * @return int - * The comparison result for uasort(). - * - * @see \Drupal\Component\Utility\SortArray::sortByTitleProperty() - * - * @deprecated as of Drupal 8.0. Use SortArray::sortByTitleProperty() instead. */ function element_sort_by_title($a, $b) { - return SortArray::sortByTitleProperty($a, $b); + $a_title = (is_array($a) && isset($a['#title'])) ? $a['#title'] : ''; + $b_title = (is_array($b) && isset($b['#title'])) ? $b['#title'] : ''; + return strnatcasecmp($a_title, $b_title); } /** @@ -4365,16 +4315,14 @@ function element_info_property($type, $property_name, $default = NULL) { * element, a default value of 0 will be used. * @param $b * Second item for comparison. - * - * @return int - * The comparison result for uasort(). - * - * @see \Drupal\Component\Utility\SortArray::sortByWeightElement() - * - * @deprecated as of Drupal 8.0. Use SortArray::sortByWeightElement() instead. */ function drupal_sort_weight($a, $b) { - return SortArray::sortByWeightElement((array) $a, (array) $b); + $a_weight = (is_array($a) && isset($a['weight'])) ? $a['weight'] : 0; + $b_weight = (is_array($b) && isset($b['weight'])) ? $b['weight'] : 0; + if ($a_weight == $b_weight) { + return 0; + } + return ($a_weight < $b_weight) ? -1 : 1; } /** @@ -4387,16 +4335,15 @@ function drupal_sort_weight($a, $b) { * that optionally include a 'title' key. * @param $b * Second item for comparison. - * - * @return int - * The comparison result for uasort(). - * - * @see \Drupal\Component\Utility\SortArray::sortByTitleElement() - * - * @deprecated as of Drupal 8.0. Use SortArray::sortByTitleElement() instead. */ function drupal_sort_title($a, $b) { - return SortArray::sortByTitleElement($a, $b); + if (!isset($b['title'])) { + return -1; + } + if (!isset($a['title'])) { + return 1; + } + return strcasecmp($a['title'], $b['title']); } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7e0e5e2..ae39080 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1745,6 +1745,20 @@ function theme_links($variables) { } /** + * Returns HTML for wrapping a dropbutton menu. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the dropbutton menu. Properties used: #children. + */ +function theme_dropbutton_wrapper($variables) { + if (!empty($variables['element']['#children'])) { + return '
' . $variables['element']['#children'] . '
'; + } +} + +/** * Returns HTML for an image. * * @param $variables @@ -2322,6 +2336,27 @@ function theme_more_link($variables) { } /** + * Returns HTML for a progress bar. + * + * Note that the core Batch API uses this only for non-JavaScript batch jobs. + * + * @param $variables + * An associative array containing: + * - percent: The percentage of the progress. + * - message: A string containing information to be displayed. + */ +function theme_progress_bar($variables) { + $output = '
'; + $output .= '
' . $variables['label'] . '
'; + $output .= '
'; + $output .= '
' . $variables['percent'] . '%
'; + $output .= '
' . $variables['message'] . '
'; + $output .= '
'; + + return $output; +} + +/** * Returns HTML for an indentation div; used for drag and drop tables. * * @param $variables @@ -2682,6 +2717,10 @@ function template_preprocess_page(&$variables) { $variables['main_menu'] = array( '#theme' =>'links__system_main_menu', '#links' => $variables['main_menu'], + '#attributes' => array( + 'id' => 'main-menu', + 'class' => array('links', 'inline', 'clearfix'), + ), '#heading' => array( 'text' => t('Main menu'), 'class' => array('visually-hidden'), @@ -2692,6 +2731,10 @@ function template_preprocess_page(&$variables) { $variables['secondary_menu'] = array( '#theme' =>'links__system_secondary_menu', '#links' => $variables['secondary_menu'], + '#attributes' => array( + 'id' => 'secondary-menu', + 'class' => array('links', 'inline', 'clearfix'), + ), '#heading' => array( 'text' => t('Secondary menu'), 'class' => array('visually-hidden'), @@ -2998,8 +3041,7 @@ function drupal_common_theme() { 'variables' => array('links' => array(), 'attributes' => array('class' => array('links')), 'heading' => array()), ), 'dropbutton_wrapper' => array( - 'variables' => array('children' => NULL), - 'template' => 'dropbutton-wrapper', + 'render element' => 'element', ), 'image' => array( // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft @@ -3040,8 +3082,7 @@ function drupal_common_theme() { 'variables' => array('url' => NULL, 'title' => NULL) ), 'progress_bar' => array( - 'variables' => array('label' => NULL, 'percent' => NULL, 'message' => NULL), - 'template' => 'progress-bar', + 'variables' => array('percent' => NULL, 'message' => NULL), ), 'indentation' => array( 'variables' => array('size' => 1), @@ -3057,12 +3098,15 @@ function drupal_common_theme() { ), 'task_list' => array( 'variables' => array('items' => NULL, 'active' => NULL, 'variant' => NULL), + 'template' => 'task-list', ), 'authorize_message' => array( 'variables' => array('message' => NULL, 'success' => TRUE), + 'template' => 'authorize-message', ), 'authorize_report' => array( - 'variables' => array('messages' => array()), + 'variables' => array('messages' => array(), 'attributes' => array()), + 'template' => 'authorize-report', ), // From pager.inc. 'pager' => array( diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 5c35ac3..be606c3 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -6,6 +6,7 @@ */ use Drupal\Component\Utility\Unicode; +use Drupal\Core\Template\Attribute; /** * Sets up the theming system for maintenance page. @@ -106,17 +107,16 @@ function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine } /** - * Returns HTML for a list of maintenance tasks to perform. + * Prepares variables for maintenance task list templates. * - * @param $variables + * Default template: task-list.html.twig. + * + * @param array $variables * An associative array containing: * - items: An associative array of maintenance tasks. * - active: The key for the currently active maintenance task. - * - variant: A variant name to be used for a CSS class. - * - * @ingroup themeable */ -function theme_task_list($variables) { +function template_preprocess_task_list(&$variables) { $items = $variables['items']; $active = $variables['active']; if (isset($variables['variant'])) { @@ -127,76 +127,59 @@ function theme_task_list($variables) { } $done = isset($items[$active]) || $active == NULL; - $output = '

Installation tasks

'; - $output .= '
    '; + $tasks = array(); foreach ($items as $k => $item) { + $tasks[$k]['item'] = $item; + $tasks[$k]['attributes'] = new Attribute(array('class' => array())); if ($active == $k) { - $class = 'active'; - $status = '(' . t('active') . ')'; + $tasks[$k]['attributes']['class'][] = 'active'; + $tasks[$k]['status'] = t('active'); $done = FALSE; } else { - $class = $done ? 'done' : ''; - $status = $done ? '(' . t('done') . ')' : ''; + $tasks[$k]['attributes']['class'][] = $done ? 'done' : ''; + $tasks[$k]['status'] = $done ? t('done') : ''; } - $output .= ''; - $output .= $item; - $output .= ($status ? ' ' . $status . '' : ''); - $output .= ''; } - $output .= '
'; - return $output; + + $variables['tasks'] = $tasks; } /** - * Returns HTML for a results report of an operation run by authorize.php. + * Prepares variables for authorize.php operation report templates. + * + * This report displays the results of an operation run via authorize.php. + * + * Default template: authorize-report.html.twig. * - * @param $variables + * @param array $variables * An associative array containing: * - messages: An array of result messages. - * - * @ingroup themeable */ -function theme_authorize_report($variables) { +function template_preprocess_authorize_report(&$variables) { $messages = $variables['messages']; - $output = ''; + + $variables['report'] = array(); if (!empty($messages)) { - $output .= '
'; + $variables['attributes']['id'] = 'authorize-results'; foreach ($messages as $heading => $logs) { $items = array(); foreach ($logs as $number => $log_message) { if ($number === '#abort') { continue; } - $items[] = theme('authorize_message', array('message' => $log_message['message'], 'success' => $log_message['success'])); + $items[] = array( + '#theme' => 'authorize_message', + '#message' => $log_message['message'], + '#success' => $log_message['success'], + ); } - $output .= theme('item_list', array('items' => $items, 'title' => $heading)); + $variables['report'][] = array( + '#theme' => 'item_list', + '#items' => $items, + '#title' => $heading, + ); } - $output .= '
'; - } - return $output; -} - -/** - * Returns HTML for a single log message from the authorize.php batch operation. - * - * @param $variables - * An associative array containing: - * - message: The log message. - * - success: A boolean indicating failure or success. - * - * @ingroup themeable - */ -function theme_authorize_message($variables) { - $message = $variables['message']; - $success = $variables['success']; - if ($success) { - $item = array('data' => $message, 'class' => array('success')); - } - else { - $item = array('data' => '' . $message . '', 'class' => array('failure')); } - return $item; } diff --git a/core/includes/update.inc b/core/includes/update.inc index d3baaef..4a17a7f 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -13,6 +13,7 @@ use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\ConfigException; use Drupal\Core\DrupalKernel; +use Drupal\Component\Gettext\PoHeader; use Drupal\Component\Uuid\Uuid; use Drupal\Component\Utility\NestedArray; use Symfony\Component\HttpFoundation\Request; @@ -480,9 +481,14 @@ function update_prepare_d8_language() { $prefixes = array(); $domains = array(); foreach ($languages as $language) { + $header = new PoHeader($language->language); + $language->formula = str_replace('$n', 'n', $language->formula); + $plural_forms = 'nplurals=' . $language->plurals . '; plural=' . $language->formula . ';'; + $parsed = $header->parsePluralForms($plural_forms); + list($nplurals, $formula) = $parsed; $plurals[$language->language] = array( - 'plurals' => $language->plurals, - 'formula' => $language->formula, + 'plurals' => $nplurals, + 'formula' => $formula, ); $javascript[$language->language] = $language->javascript; $prefixes[$language->language] = $language->prefix; diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 672a0d1..db2552b 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -251,7 +251,7 @@ public static function queue($name, $reliable = FALSE) { /** * Returns a key/value storage collection. * - * @param string $collection + * @param $collection * Name of the key/value collection to return. * * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface @@ -305,10 +305,10 @@ public static function entityQuery($entity_type, $conjunction = 'AND') { /** * Returns the entity query aggregate object for this entity type. * - * @param string $entity_type + * @param $entity_type * The entity type, e.g. node, for which the query object should be * returned. - * @param string $conjunction + * @param $conjunction * AND if all conditions in the query need to apply, OR if any of them is * enough. Optional, defaults to AND. * @@ -331,7 +331,7 @@ public static function flood() { /** * Returns the module handler. * - * @return \Drupal\Core\Extension\ModuleHandlerInterface + * @return \Drupal\Core\Extension\ModuleHandler */ public static function moduleHandler() { return static::$container->get('module_handler'); @@ -364,7 +364,7 @@ public static function token() { /** * Returns the url generator service. * - * @return \Drupal\Core\Routing\PathBasedGeneratorInterface + * @return \Drupal\Core\Routing\UrlGenerator * The url generator service. */ public static function urlGenerator() { diff --git a/core/lib/Drupal/Component/Gettext/PoHeader.php b/core/lib/Drupal/Component/Gettext/PoHeader.php index 44959eb..421ddd7 100644 --- a/core/lib/Drupal/Component/Gettext/PoHeader.php +++ b/core/lib/Drupal/Component/Gettext/PoHeader.php @@ -189,10 +189,14 @@ public function __toString() { * The Plural-Forms entry value. * * @return - * An array containing the number of plural forms and the converted version - * of the formula that can be evaluated with PHP later. + * An indexed array of parsed plural formula data. Containing: + * - 'nplurals': The number of plural forms defined by the plural formula. + * - 'plurals': Array of plural positions keyed by plural value. + * + * @throws Exception */ function parsePluralForms($pluralforms) { + $plurals = array(); // First, delete all whitespace. $pluralforms = strtr($pluralforms, array(" " => "", "\t" => "")); @@ -214,14 +218,31 @@ function parsePluralForms($pluralforms) { return FALSE; } - // Get PHP version of the plural formula. - $plural = $this->parseArithmetic($plural); + // If the number of plurals is zero, we return a default result. + if ($nplurals == 0) { + return array($nplurals, array('default' => 0)); + } + + // Calculate possible plural positions of different plural values. All known + // plural formula's are repetitive above 100. + // For data compression we store the last position the array value + // changes and store it as default. + $element_stack = $this->parseArithmetic($plural); + $default = 0; + if ($element_stack !== FALSE) { + for ($i = 0; $i <= 199; $i++) { + $plurals[$i] = $this->evaluatePlural($element_stack, $i); + } + $default = $plurals[$i - 1]; + $plurals = array_filter($plurals, function ($value) use ($default) { + return ($value != $default); + }); + $plurals['default'] = $default; - if ($plural !== FALSE) { - return array($nplurals, $plural); + return array($nplurals, $plurals); } else { - throw new Exception('The plural formula could not be parsed.'); + throw new \Exception('The plural formula could not be parsed.'); } } @@ -247,7 +268,7 @@ private function parseHeader($header) { } /** - * Parses and sanitizes an arithmetic formula into a PHP expression. + * Parses and sanitizes an arithmetic formula into a plural element stack. * * While parsing, we ensure, that the operators have the right * precedence and associativity. @@ -256,7 +277,7 @@ private function parseHeader($header) { * A string containing the arithmetic formula. * * @return - * A version of the formula to evaluate with PHP later. + * A stack of values and operations to be evaluated. */ private function parseArithmetic($string) { // Operator precedence table. @@ -319,8 +340,9 @@ private function parseArithmetic($string) { $element_stack[] = $topop; $topop = array_pop($operator_stack); } + $return = $element_stack; - // Now extract formula from stack. + // Now validate stack. $previous_size = count($element_stack) + 1; while (count($element_stack) < $previous_size) { $previous_size = count($element_stack); @@ -344,12 +366,7 @@ private function parseArithmetic($string) { } // If only one element is left, the number of operators is appropriate. - if (count($element_stack) == 1) { - return $element_stack[0]; - } - else { - return FALSE; - } + return count($element_stack) == 1 ? $return : FALSE; } /** @@ -417,4 +434,136 @@ private function tokenizeFormula($formula) { return $tokens; } + /** + * Evaluate the plural element stack using a plural value. + * + * Using an element stack, which represents a plural formula, we calculate + * which plural string should be used for a given plural value. + * + * An example of plural formula parting and evaluation: + * Plural formula: 'n!=1' + * This formula is parsed by parseArithmetic() to a stack (array) of elements: + * array( + * 0 => '$n', + * 1 => '1', + * 2 => '!=', + * ); + * The evaluatePlural() method evaluates the $element_stack using the plural + * value $n. Before the actual evaluation, the '$n' in the array is replaced + * by the value of $n. + * For example: $n = 2 results in: + * array( + * 0 => '2', + * 1 => '1', + * 2 => '!=', + * ); + * The stack is processed until only one element is (the result) is left. In + * every iteration the top elements of the stack, up until the first operator, + * are evaluated. After evaluation the arguments and the operator itself are + * removed and replaced by the evaluation result. This is typically 2 + * arguments and 1 element for the operator. + * Because the operator is '!=' the example stack is evaluated as: + * $f = (int) 2 != 1; + * The resulting stack is: + * array( + * 0 => 1, + * ); + * With only one element left in the stack (the final result) the loop is + * terminated and the result is returned. + * + * @param array $element_stack + * Array of plural formula values and operators create by parseArithmetic(). + * @param integer $n + * The @count number for which we are determining the right plural position. + * + * @return integer + * Number of the plural string to be used for the given plural value. + * + * @see parseArithmetic() + * @throws Exception + */ + protected function evaluatePlural($element_stack, $n) { + $count = count($element_stack); + $limit = $count; + // Replace the '$n' value in the formula by the plural value. + for ($i = 0; $i < $count; $i++) { + if ($element_stack[$i] === '$n') { + $element_stack[$i] = $n; + } + } + + // We process the stack until only one element is (the result) is left. + // We limit the number of evaluation cycles to prevent an endless loop in + // case the stack contains an error. + while (isset($element_stack[1])) { + for ($i = 2; $i < $count; $i++) { + // There's no point in checking non-symbols. Also, switch(TRUE) would + // match any case and so it would break. + if (is_bool($element_stack[$i]) || is_numeric($element_stack[$i])) { + continue; + } + $f = NULL; + $length = 3; + $delta = 2; + switch ($element_stack[$i]) { + case '==': + $f = $element_stack[$i - 2] == $element_stack[$i - 1]; + break; + case '!=': + $f = $element_stack[$i - 2] != $element_stack[$i - 1]; + break; + case '<=': + $f = $element_stack[$i - 2] <= $element_stack[$i - 1]; + break; + case '>=': + $f = $element_stack[$i - 2] >= $element_stack[$i - 1]; + break; + case '<': + $f = $element_stack[$i - 2] < $element_stack[$i - 1]; + break; + case '>': + $f = $element_stack[$i - 2] > $element_stack[$i - 1]; + break; + case '+': + $f = $element_stack[$i - 2] + $element_stack[$i - 1]; + break; + case '-': + $f = $element_stack[$i - 2] - $element_stack[$i - 1]; + break; + case '*': + $f = $element_stack[$i - 2] * $element_stack[$i - 1]; + break; + case '/': + $f = $element_stack[$i - 2] / $element_stack[$i - 1]; + break; + case '%': + $f = $element_stack[$i - 2] % $element_stack[$i - 1]; + break; + case '&&': + $f = $element_stack[$i - 2] && $element_stack[$i - 1]; + break; + case '||': + $f = $element_stack[$i - 2] || $element_stack[$i - 1]; + break; + case ':': + $f = $element_stack[$i - 3] ? $element_stack[$i - 2] : $element_stack[$i - 1]; + // This operator has 3 preceding elements, instead of the default 2. + $length = 5; + $delta = 3; + break; + } + + // If the element is an operator we remove the processed elements and + // store the result. + if (isset($f)) { + array_splice($element_stack, $i - $delta, $length, $f); + break; + } + } + } + if (!$limit) { + throw new \Exception('The plural formula could not be evaluated.'); + } + return (int) $element_stack[0]; + } } diff --git a/core/lib/Drupal/Component/Utility/SortArray.php b/core/lib/Drupal/Component/Utility/SortArray.php deleted file mode 100644 index 92763f5..0000000 --- a/core/lib/Drupal/Component/Utility/SortArray.php +++ /dev/null @@ -1,137 +0,0 @@ -container->get('plugin.manager.entity'); - } - - /** - * Returns the requested cache bin. - * - * @param string $bin - * (optional) The cache bin for which the cache object should be returned, - * defaults to 'cache'. - * - * @return \Drupal\Core\Cache\CacheBackendInterface - * The cache object associated with the specified bin. - */ - protected function cache($bin = 'cache') { - return $this->container->get('cache.' . $bin); - } - - /** - * Retrieves a configuration object. - * - * This is the main entry point to the configuration API. Calling - * @code $this->config('book.admin') @endcode will return a configuration - * object in which the book module can store its administrative settings. - * - * @param string $name - * The name of the configuration object to retrieve. The name corresponds to - * a configuration file. For @code config('book.admin') @endcode, the config - * object returned will contain the contents of book.admin configuration file. - * - * @return \Drupal\Core\Config\Config - * A configuration object. - */ - protected function config($name) { - return $this->container->get('config.factory')->get($name); - } - - /** - * Returns a key/value storage collection. - * - * @param string $collection - * Name of the key/value collection to return. - * - * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface - */ - protected function keyValue($collection) { - return $this->container->get('keyvalue')->get($collection); - } - - /** - * Returns the state storage service. - * - * Use this to store machine-generated data, local to a specific environment - * that does not need deploying and does not need human editing; for example, - * the last time cron was run. Data which needs to be edited by humans and - * needs to be the same across development, production, etc. environments - * (for example, the system maintenance message) should use config() instead. - * - * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface - */ - protected function state() { - return $this->container->get('state'); - } - - /** - * Returns the module handler. - * - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected function moduleHandler() { - return $this->container->get('module_handler'); - } - - /** - * Returns the url generator service. - * - * @return \Drupal\Core\Routing\PathBasedGeneratorInterface - * The url generator service. - */ - protected function urlGenerator() { - return $this->container->get('url_generator'); - } - - /** - * Translates a string to the current language or to a given language using - * the string translation service. - * - * @param string $string - * A string containing the English string to translate. - * @param array $args - * An associative array of replacements to make after translation. Based - * on the first character of the key, the value is escaped and/or themed. - * See \Drupal\Core\Utility\String::format() for details. - * @param array $options - * An associative array of additional options, with the following elements: - * - 'langcode': The language code to translate to a language other than - * what is used to display the page. - * - 'context': The context the source string belongs to. - * - * @return string - * The translated string. - */ - protected function t($string, array $args = array(), array $options = array()) { - return $this->container->get('string_translation')->translate($string, $args, $options); - } - - /** - * Returns the language manager service. - * - * @return \Drupal\Core\Language\LanguageManager - * The language manager. - */ - protected function languageManager() { - return $this->container->get('language_manager'); - } - - /** - * Returns a redirect response object for the specified - * - * @param string $route_name - * The name of the route to which to redirect. - * @param array $parameters - * Parameters for the route. - * @param int $status - * The HTTP redirect status code for the redirect. The default is 302 Found. - * @return \Symfony\Component\HttpFoundation\RedirectResponse - * A redirect response object that may be returned by the controller. - */ - public function redirect($route_name, array $parameters = array(), $status = 302) { - $url = $this->container->get('url_generator')->generate($route_name, $parameters, TRUE); - return new RedirectResponse($url, $status); - } -} diff --git a/core/lib/Drupal/Core/Controller/ControllerInterface.php b/core/lib/Drupal/Core/Controller/ControllerInterface.php index 5cdab2e..f87ae12 100644 --- a/core/lib/Drupal/Core/Controller/ControllerInterface.php +++ b/core/lib/Drupal/Core/Controller/ControllerInterface.php @@ -11,14 +11,6 @@ /** * Defines a common interface for route controllers. - * - * This interface gives controller classes a factory method for instantiation - * rather than relying on a services.yml entry. However, it may result in - * a lot of boilerplate code in the class. As an alternative, controllers that - * contain only limited glue code ("thin" controllers) should instead extend - * ControllerBase as that allows direct access to the container. That renders - * the controller very difficult to unit test so should only be used for - * controllers that are trivial in complexity. */ interface ControllerInterface { diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Insert.php b/core/lib/Drupal/Core/Database/Driver/mysql/Insert.php index c41fdf3..233118c 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Insert.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Insert.php @@ -49,8 +49,7 @@ public function __toString() { // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - $insert_fields_string = $insert_fields ? ' (' . implode(', ', $insert_fields) . ') ' : ' '; - return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; } $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Insert.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Insert.php index 998cbb7..dffa1fd 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Insert.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Insert.php @@ -118,8 +118,7 @@ public function __toString() { // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - $insert_fields_string = $insert_fields ? ' (' . implode(', ', $insert_fields) . ') ' : ' '; - return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; } $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Insert.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Insert.php index 3a0bcaa..e584943 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Insert.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Insert.php @@ -40,11 +40,10 @@ public function __toString() { // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - $insert_fields_string = $this->insertFields ? ' (' . implode(', ', $this->insertFields) . ') ' : ' '; - return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery; } return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')'; } -} +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Database/Query/Insert.php b/core/lib/Drupal/Core/Database/Query/Insert.php index 72cadb8..7862436 100644 --- a/core/lib/Drupal/Core/Database/Query/Insert.php +++ b/core/lib/Drupal/Core/Database/Query/Insert.php @@ -284,11 +284,10 @@ public function preExecute() { // first call to fields() does have an effect. $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions()))); } - else { - // Don't execute query without fields. - if (count($this->insertFields) + count($this->defaultFields) == 0) { - throw new NoFieldsException('There are no fields available to insert with.'); - } + + // Don't execute query without fields. + if (count($this->insertFields) + count($this->defaultFields) == 0) { + throw new NoFieldsException('There are no fields available to insert with.'); } // If no values have been added, silently ignore this query. This can happen diff --git a/core/lib/Drupal/Core/Entity/Controller/EntityListController.php b/core/lib/Drupal/Core/Entity/Controller/EntityListController.php index 7a07a38..5edbca1 100644 --- a/core/lib/Drupal/Core/Entity/Controller/EntityListController.php +++ b/core/lib/Drupal/Core/Entity/Controller/EntityListController.php @@ -7,13 +7,40 @@ namespace Drupal\Core\Entity\Controller; -use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Entity\EntityManager; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Defines a generic controller to list entities. */ -class EntityListController extends ControllerBase { +class EntityListController implements ControllerInterface { + + /** + * The entity manager + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** + * Creates an EntityListController object. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + */ + public function __construct(EntityManager $entity_manager) { + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity') + ); + } /** * Provides the listing page for any entity type. @@ -25,7 +52,7 @@ class EntityListController extends ControllerBase { * A render array as expected by drupal_render(). */ public function listing($entity_type) { - return $this->entityManager()->getListController($entity_type)->render(); + return $this->entityManager->getListController($entity_type)->render(); } } diff --git a/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php index 09cc203..88b2207 100644 --- a/core/lib/Drupal/Core/Entity/EntityListController.php +++ b/core/lib/Drupal/Core/Entity/EntityListController.php @@ -9,7 +9,6 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\Component\Utility\String; /** * Provides a generic implementation of an entity list controller. @@ -145,7 +144,7 @@ public function buildHeader() { * @see Drupal\Core\Entity\EntityListController::render() */ public function buildRow(EntityInterface $entity) { - $row['label'] = String::checkPlain($entity->label()); + $row['label'] = $entity->label(); $row['id'] = $entity->id(); $operations = $this->buildOperations($entity); $row['operations']['data'] = $operations; diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index 0378b95..a11ddf4 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -221,15 +221,15 @@ Drupal.jsAC.prototype.found = function (matches) { } // Prepare matches. + var ul = $(''); var ac = this; - var ul = $('') - .on('mousedown', 'li', function (e) { ac.select(this); }) - .on('mouseover', 'li', function (e) { ac.highlight(this); }) - .on('mouseout', 'li', function (e) { ac.unhighlight(this); }); for (var key in matches) { if (matches.hasOwnProperty(key)) { $('
  • ') .html($('
    ').html(matches[key])) + .mousedown(function () { ac.select(this); }) + .mouseover(function () { ac.highlight(this); }) + .mouseout(function () { ac.unhighlight(this); }) .data('autocompleteValue', key) .appendTo(ul); } diff --git a/core/misc/dialog.ajax.js b/core/misc/dialog.ajax.js index c56515b..8bb6599 100644 --- a/core/misc/dialog.ajax.js +++ b/core/misc/dialog.ajax.js @@ -33,6 +33,9 @@ } } }, + detach: function (context, settings) { + $(context).find('form').off('submit.dialogSubmit'); + }, /** * Scan a dialog for any primary buttons and move them to the button area. @@ -66,6 +69,12 @@ } }); }); + if ($buttons.length) { + $dialog.find('form').on('submit.dialogSubmit', function (e) { + $buttons.first().trigger('click'); + e.preventDefault(); + }); + } return buttons; } }; diff --git a/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php b/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php new file mode 100644 index 0000000..3d9f92e --- /dev/null +++ b/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php @@ -0,0 +1,116 @@ + 'exclude', + ); + $options['selected_actions'] = array( + 'default' => array(), + ); + return $options; + } + + /** + * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['include_exclude'] = array( + '#type' => 'radios', + '#title' => t('Available actions'), + '#options' => array( + 'exclude' => t('All actions, except selected'), + 'include' => t('Only selected actions'), + ), + '#default_value' => $this->options['include_exclude'], + ); + $form['selected_actions'] = array( + '#type' => 'checkboxes', + '#title' => t('Selected actions'), + '#options' => $this->getBulkOptions(FALSE), + '#default_value' => $this->options['selected_actions'], + ); + } + + /** + * Overrides \Drupal\views\Plugin\views\PluginBase::buildOptionsForm(). + */ + public function validateOptionsForm(&$form, &$form_state) { + parent::validateOptionsForm($form, $form_state); + + $form_state['values']['options']['selected_actions'] = array_filter($form_state['values']['options']['selected_actions']); + } + + /** + * Implements \Drupal\system\Plugin\views\field\BulkFormBase::getBulkOptions(). + * + * @param bool $filtered + * (optional) Whether to filter actions to selected actions. + */ + protected function getBulkOptions($filtered = TRUE) { + // Get all available actions. + $entity_type = $this->getEntityType(); + $options = array(); + // Filter the action list. + foreach ($this->actions as $id => $action) { + if ($filtered) { + $in_selected = in_array($id, $this->options['selected_actions']); + // If the field is configured to include only the selected actions, + // skip actions that were not selected. + if (($this->options['include_exclude'] == 'include') && !$in_selected) { + continue; + } + // Otherwise, if the field is configured to exclude the selected + // actions, skip actions that were selected. + elseif (($this->options['include_exclude'] == 'exclude') && $in_selected) { + continue; + } + } + // Only allow actions that are valid for this entity type. + if (($action->getType() == $entity_type)) { + $options[$id] = $action->label(); + } + } + + return $options; + } + + /** + * Implements \Drupal\system\Plugin\views\field\BulkFormBase::views_form_submit(). + */ + public function views_form_submit(&$form, &$form_state) { + parent::views_form_submit($form, $form_state); + if ($form_state['step'] == 'views_form_views_form') { + $count = count(array_filter($form_state['values'][$this->options['id']])); + $action = $this->actions[$form_state['values']['action']]; + if ($count) { + drupal_set_message(format_plural($count, '%action was applied to @count item.', '%action was applied to @count items.', array( + '%action' => $action->label(), + ))); + } + } + } + +} diff --git a/core/modules/aggregator/aggregator.pages.inc b/core/modules/aggregator/aggregator.pages.inc index 0fa2bd8..98dc665 100644 --- a/core/modules/aggregator/aggregator.pages.inc +++ b/core/modules/aggregator/aggregator.pages.inc @@ -157,7 +157,7 @@ function _aggregator_page_list($items, $op, $feed_source = '') { $build['feed_source'] = is_array($feed_source) ? $feed_source : array('#markup' => $feed_source); if ($items) { $build['items'] = entity_view_multiple($items, 'default'); - $build['pager'] = array('#theme' => 'pager'); + $build['pager']['#markup'] = theme('pager'); } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php index 98709ea..7239213 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php @@ -99,13 +99,11 @@ public function render($row) { ), ); - $build = array( - '#theme' => $this->themeFunctions(), - '#view' => $this->view, - '#options' => $this->options, - '#row' => $item, - ); - return drupal_render($build); + return theme($this->themeFunctions(), array( + 'view' => $this->view, + 'options' => $this->options, + 'row' => $item, + )); } } diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 8eeb57f..9820348 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -61,14 +61,14 @@ function block_help($path, $arg) { $output .= '
    ' . t('Blocks can be configured to be visible only on certain pages, only to users of certain roles, or only on pages displaying certain content types. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.', array('@content-type' => url('admin/structure/types'), '@user' => url('user'))) . '
    '; if (module_exists('custom_block')) { $output .= '
    ' . t('Creating custom blocks') . '
    '; - $output .= '
    ' . t('Users with the Administer blocks permission can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/list/' . config('system.theme')->get('default') . '/add/custom_blocks'))) . '
    '; + $output .= '
    ' . t('Users with the Administer blocks permission can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add/custom_blocks'))) . '
    '; } $output .= ''; return $output; } if ($arg[0] == 'admin' && $arg[1] == 'structure' && $arg['2'] == 'block' && (empty($arg[3]) || $arg[3] == 'list') && empty($arg[5])) { if (!empty($arg[4])) { - $demo_theme = $arg[4]; + list(, $demo_theme) = explode(':', $arg[4]); } else { $demo_theme = config('system.theme')->get('default'); @@ -136,28 +136,28 @@ function block_menu() { // and plugin IDs to decouple the routes from these dependencies and allow // hook_menu_local_tasks() to check for the untranslated tab_parent path. // @see http://drupal.org/node/1067408 - foreach (list_themes() as $key => $theme) { - $items["admin/structure/block/list/$key"] = array( - 'title' => check_plain($theme->info['name']), - 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, - 'route_name' => "block_admin_display.$key", - ); - $items["admin/structure/block/list/$key/add"] = array( - 'title' => 'Place blocks', - 'type' => MENU_LOCAL_ACTION, - 'route_name' => "block_plugin_ui.$key", - ); - $items["admin/structure/block/demo/$key"] = array( - 'title' => check_plain($theme->info['name']), - 'page callback' => 'block_admin_demo', - 'page arguments' => array($key), - 'type' => MENU_CALLBACK, - 'access callback' => '_block_themes_access', - 'access arguments' => array($key), - 'theme callback' => '_block_custom_theme', - 'theme arguments' => array($key), - 'file' => 'block.admin.inc', - ); + $themes = list_themes(); + foreach (drupal_container()->get('plugin.manager.system.plugin_ui')->getDefinitions() as $plugin_id => $plugin) { + list($plugin_base, $key) = explode(':', $plugin_id); + if ($plugin_base == 'block_plugin_ui') { + $theme = $themes[$key]; + $items['admin/structure/block/list/' . $plugin_id] = array( + 'title' => check_plain($theme->info['name']), + 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, + 'route_name' => 'block_admin_display.' . $plugin_id + ); + $items['admin/structure/block/demo/' . $key] = array( + 'title' => check_plain($theme->info['name']), + 'page callback' => 'block_admin_demo', + 'page arguments' => array($key), + 'type' => MENU_CALLBACK, + 'access callback' => '_block_themes_access', + 'access arguments' => array($key), + 'theme callback' => '_block_custom_theme', + 'theme arguments' => array($key), + 'file' => 'block.admin.inc', + ); + } } return $items; } diff --git a/core/modules/block/block.routing.yml b/core/modules/block/block.routing.yml index 669e0a2..cabd4cb 100644 --- a/core/modules/block/block.routing.yml +++ b/core/modules/block/block.routing.yml @@ -26,10 +26,3 @@ block_admin_add: _content: '\Drupal\block\Controller\BlockAddController::blockAddConfigureForm' requirements: _permission: 'administer blocks' - -block_autocomplete: - pattern: '/block/autocomplete' - defaults: - _controller: '\Drupal\block\Controller\BlockAutocompleteController::autocomplete' - requirements: - _permission: 'administer blocks' diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index b361dba..9b6b40d 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -13,6 +13,7 @@ services: class: Drupal\block\Routing\RouteSubscriber tags: - { name: event_subscriber} + arguments: ['@plugin.manager.system.plugin_ui'] block.theme_access_check: class: Drupal\block\Access\BlockThemeAccessCheck tags: diff --git a/core/modules/block/lib/Drupal/block/BlockFormController.php b/core/modules/block/lib/Drupal/block/BlockFormController.php index 6041b2c..0386277 100644 --- a/core/modules/block/lib/Drupal/block/BlockFormController.php +++ b/core/modules/block/lib/Drupal/block/BlockFormController.php @@ -283,7 +283,7 @@ public function submit(array $form, array &$form_state) { drupal_set_message(t('The block configuration has been saved.')); cache_invalidate_tags(array('content' => TRUE)); - $form_state['redirect'] = 'admin/structure/block/list/' . $entity->get('theme'); + $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $entity->get('theme'); } /** diff --git a/core/modules/block/lib/Drupal/block/Controller/BlockAddController.php b/core/modules/block/lib/Drupal/block/Controller/BlockAddController.php index 0d065eb..f7f4e38 100644 --- a/core/modules/block/lib/Drupal/block/Controller/BlockAddController.php +++ b/core/modules/block/lib/Drupal/block/Controller/BlockAddController.php @@ -7,13 +7,33 @@ namespace Drupal\block\Controller; -use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Entity\EntityManager; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Controller for building the block instance add form. */ -class BlockAddController extends ControllerBase { +class BlockAddController implements ControllerInterface { + + /** + * Constructs a Block object. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * Entity manager service. + */ + public function __construct(EntityManager $entity_manager) { + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity') + ); + } /** * Build the block instance add form. @@ -31,9 +51,8 @@ public function blockAddConfigureForm($plugin_id, $theme) { drupal_set_title(t('Configure block')); // Create a block entity. - $entity = $this->entityManager()->getStorageController('block')->create(array('plugin' => $plugin_id, 'theme' => $theme)); + $entity = $this->entityManager->getStorageController('block')->create(array('plugin' => $plugin_id, 'theme' => $theme)); - return $this->entityManager()->getForm($entity); + return $this->entityManager->getForm($entity); } - } diff --git a/core/modules/block/lib/Drupal/block/Controller/BlockAutocompleteController.php b/core/modules/block/lib/Drupal/block/Controller/BlockAutocompleteController.php deleted file mode 100644 index 539a8c1..0000000 --- a/core/modules/block/lib/Drupal/block/Controller/BlockAutocompleteController.php +++ /dev/null @@ -1,78 +0,0 @@ -manager = $manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.block') - ); - } - - /** - * Autocompletes a block plugin ID. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * - * @return \Symfony\Component\HttpFoundation\JsonResponse - * The matched plugins as JSON. - */ - public function autocomplete(Request $request) { - $string_typed = $request->query->get('q'); - // The passed string may be comma or space separated. - $string_typed = Tags::explode($string_typed); - // Take the last result and lowercase it. - $string = Unicode::strtolower(array_pop($string_typed)); - $matches = array(); - if ($string) { - $titles = array(); - // Gather all block plugins and their admin titles. - foreach($this->manager->getDefinitions() as $plugin_id => $plugin) { - $titles[$plugin_id] = $plugin['admin_label']; - } - // Find any matching block plugin IDs. - $matches = preg_grep("/\b". $string . "/i", $titles); - } - - return new JsonResponse($matches); - } - -} diff --git a/core/modules/block/lib/Drupal/block/Controller/BlockListController.php b/core/modules/block/lib/Drupal/block/Controller/BlockListController.php index f31e652..23b145c 100644 --- a/core/modules/block/lib/Drupal/block/Controller/BlockListController.php +++ b/core/modules/block/lib/Drupal/block/Controller/BlockListController.php @@ -9,6 +9,8 @@ use Drupal\Core\Entity\Controller\EntityListController; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Entity\EntityManager; /** * Defines a controller to list blocks. @@ -16,17 +18,50 @@ class BlockListController extends EntityListController { /** + * The configuration factory object. + * + * @var \Drupal\Core\Config\ConfigFactory + */ + protected $configFactory; + + + /** + * Creates an BlockListController object. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * Configuration factory object. + */ + public function __construct(EntityManager $entity_manager, ConfigFactory $config_factory) { + $this->entityManager = $entity_manager; + $this->configFactory = $config_factory; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity'), + $container->get('config.factory') + ); + } + + /** * Shows the block administration page. * + * @param string $entity_type + * Entity type of list page. * @param string|null $theme * Theme key of block list. * * @return array * A render array as expected by drupal_render(). */ - public function listing($theme = NULL) { - $theme = $theme ?: $this->config('system.theme')->get('default'); - return $this->entityManager()->getListController('block')->render($theme); + public function listing($entity_type, $theme = NULL) { + $default_theme = $theme ?: $this->configFactory->get('system.theme')->get('default'); + return $this->entityManager->getListController($entity_type)->render($default_theme); } } diff --git a/core/modules/block/lib/Drupal/block/Form/PlaceBlocksForm.php b/core/modules/block/lib/Drupal/block/Form/PlaceBlocksForm.php deleted file mode 100644 index ffc4b9e..0000000 --- a/core/modules/block/lib/Drupal/block/Form/PlaceBlocksForm.php +++ /dev/null @@ -1,157 +0,0 @@ -manager = $manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.block') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormID() { - return 'block_plugin_ui'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, array &$form_state, $theme = NULL, $category = NULL) { - $this->theme = $theme; - $form['#theme'] = 'system_plugin_ui_form'; - $rows = array(); - $categories = array(); - foreach ($this->manager->getDefinitions() as $plugin_id => $plugin_definition) { - if (empty($category) || $plugin_definition['category'] == $category) { - $rows[$plugin_id] = $this->row($plugin_id, $plugin_definition); - } - $categories[$plugin_definition['category']] = array( - 'title' => $plugin_definition['category'], - 'href' => 'admin/structure/block/list/' . $this->theme . '/add/' . $plugin_definition['category'], - ); - } - - $form['right']['block'] = array( - '#type' => 'textfield', - '#title' => t('Search'), - '#autocomplete_path' => 'block/autocomplete', - ); - $form['right']['submit'] = array( - '#type' => 'submit', - '#button_type' => 'primary', - '#value' => t('Next'), - ); - $form['right']['all_plugins'] = array( - '#type' => 'link', - '#title' => t('All blocks'), - '#href' => 'admin/structure/block/list/' . $this->theme . '/add', - ); - if (!empty($categories)) { - $form['right']['categories'] = array( - '#theme' => 'links', - '#heading' => array( - 'text' => t('Categories'), - 'level' => 'h3', - ), - '#links' => $categories, - ); - } - - // Sort rows alphabetically. - asort($rows); - $form['left']['plugin_library'] = array( - '#theme' => 'table', - '#header' => array(t('Subject'), t('Operations')), - '#rows' => $rows, - ); - return $form; - } - - /** - * Generates the row data for a single block plugin. - * - * @param string $plugin_id - * The plugin ID. - * @param array $plugin_definition - * The plugin definition. - * - * @return array - * The row data for a single block plugin. - */ - protected function row($plugin_id, array $plugin_definition) { - $row = array(); - $row[] = String::checkPlain($plugin_definition['admin_label']); - $row[] = array('data' => array( - '#type' => 'operations', - '#links' => array( - 'configure' => array( - 'title' => t('Place block'), - 'href' => 'admin/structure/block/add/' . $plugin_id . '/' . $this->theme, - ), - ), - )); - return $row; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, array &$form_state) { - if (!$this->manager->getDefinition($form_state['values']['block'])) { - form_set_error('block', t('You must select a valid block.')); - } - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, array &$form_state) { - $form_state['redirect'] = 'admin/structure/block/add/' . $form_state['values']['block'] . '/' . $this->theme; - } - -} diff --git a/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php new file mode 100644 index 0000000..aaeef6e --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php @@ -0,0 +1,29 @@ + $theme) { + $this->derivatives[$key] = $base_plugin_definition; + } + return parent::getDerivativeDefinitions($base_plugin_definition); + } +} diff --git a/core/modules/block/lib/Drupal/block/Plugin/PluginUI/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/PluginUI/BlockPluginUI.php new file mode 100644 index 0000000..de7e079 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Plugin/PluginUI/BlockPluginUI.php @@ -0,0 +1,229 @@ +getPluginId()); + $plugin_definition = $this->getPluginDefinition(); + // @todo Find out how to let the manager be injected into the class. + $manager = drupal_container()->get($plugin_definition['manager']); + $plugins = $manager->getDefinitions(); + $form['#theme'] = 'system_plugin_ui_form'; + $form['theme'] = array( + '#type' => 'value', + '#value' => $theme, + ); + $form['manager'] = array( + '#type' => 'value', + '#value' => $manager, + ); + $form['instance'] = array( + '#type' => 'value', + '#value' => $this, + ); + $form['right']['block'] = array( + '#type' => 'textfield', + '#title' => t('Search'), + '#autocomplete_path' => 'system/autocomplete/' . $this->getPluginId(), + ); + $form['right']['submit'] = array( + '#type' => 'submit', + '#value' => t('Next'), + ); + $rows = array(); + foreach ($plugins as $plugin_id => $display_plugin_definition) { + if (empty($facet) || $this->facetCompare($facet, $display_plugin_definition)) { + $rows[$plugin_id] = $this->row($plugin_id, $display_plugin_definition); + } + foreach ($plugin_definition['facets'] as $key => $title) { + $facets[$key][$display_plugin_definition[$key]] = $this->facetLink($key, $plugin_id, $display_plugin_definition); + } + $form['right']['all_plugins'] = array( + '#type' => 'link', + '#title' => $plugin_definition['all_plugins'], + '#href' => $this->allPluginsUrl($plugin_id, $display_plugin_definition), + ); + foreach ($facets as $group => $values) { + $form['right'][$group] = array( + '#theme' => 'links', + '#heading' => array( + 'text' => $plugin_definition['facets'][$group], + 'level' => 'h3', + ), + '#links' => $values, + ); + } + } + // Sort rows alphabetically. + asort($rows); + $form['left']['plugin_library'] = array( + '#theme' => 'table', + '#header' => $this->tableHeader(), + '#rows' => $rows, + ); + return $form; + } + + /** + * Overrides \Drupal\system\Plugin\PluginUIBase::formValidate(). + */ + public function formValidate($form, &$form_state) { + $definitions = $form_state['values']['manager']->getDefinitions(); + if (!isset($definitions[$form_state['values']['block']])) { + form_set_error('block', t('You must select a valid block.')); + } + } + + /** + * Overrides \Drupal\system\Plugin\PluginUIBase::formSubmit(). + */ + public function formSubmit($form, &$form_state) { + $form_state['redirect'] = 'admin/structure/block/add/' . $form_state['values']['block'] . '/' . $form_state['values']['theme']; + } + + /** + * Overrides \Drupal\system\Plugin\PluginUIBase::access(). + */ + public function access() { + list($plugin, $theme) = explode(':', $this->getPluginId()); + return _block_themes_access($theme); + } + + /** + * Overrides \Drupal\system\Plugin\PluginUIBase::tableHeader(). + */ + public function tableHeader() { + return array(t('Subject'), t('Operations')); + } + + /** + * Overrides \Drupal\system\Plugin\PluginUIBase::row(). + */ + public function row($display_plugin_id, array $display_plugin_definition) { + $plugin_definition = $this->getPluginDefinition(); + list($plugin, $theme) = explode(':', $this->getPluginId()); + $row = array(); + $row[] = check_plain($display_plugin_definition['admin_label']); + $row[] = array('data' => array( + '#type' => 'operations', + '#links' => array( + 'configure' => array( + 'title' => $plugin_definition['link_title'], + 'href' => $plugin_definition['config_path'] . '/' . $display_plugin_id . '/' . $theme, + ), + ), + )); + return $row; + } + + /** + * Creates a facet link for a given facet of a display plugin. + * + * Provides individually formatted links for the faceting that happens within + * the user interface. Since this is a faceting style procedure, each plugin + * may be parsed multiple times in order to extract all facets and their + * appropriate labels. + * + * The $display_plugin_id and $display_plugin_definition are provided for + * convenience when overriding this method. + * + * @param string $facet + * A simple string indicating what element of the $display_plugin_definition + * to utilize for faceting. + * @param string $display_plugin_id + * The plugin ID of the plugin we are currently parsing a facet link from. + * @param array $display_plugin_definition + * The plugin definition we are parsing. + * + * @return array + * Returns a row array comaptible with theme_links(). + */ + protected function facetLink($facet, $display_plugin_id, array $display_plugin_definition) { + $plugin_definition = $this->getPluginDefinition(); + return array( + 'title' => $display_plugin_definition[$facet], + 'href' => $plugin_definition['path'] . '/' . $this->getPluginId() . '/' . $facet . ':' . $display_plugin_definition[$facet], + ); + } + + /** + * Determines whether a given facet should be displayed for a plugin. + * + * Compares a given plugin definition with the selected facet to determine if + * the plugin should be displayed in the user interface. + * + * @param string $facet + * A colon separated string representing the key/value paring of a selected + * facet. + * @param array $display_plugin_definition + * The plugin definition to be compared. + * + * @return bool + * Returns TRUE if the selected facet matches this plugin. + */ + protected function facetCompare($facet, $display_plugin_definition) { + list($facet_type, $option) = explode(':', $facet); + return $option == $display_plugin_definition[$facet_type]; + } + + /** + * Provides an "all" style link to reset the facets. + * + * The $display_plugin_id and $display_plugin_definition are provided for + * convenience when overriding this method. + * + * @param string $display_plugin_id + * The plugin ID of the plugin we are currently parsing a facet link from. + * @param array $display_plugin_definition + * The plugin definition we are parsing. + * + * @return string + * Returns a simple URL string for use within l(). + */ + protected function allPluginsUrl($display_plugin_id, $display_plugin_definition) { + $plugin_definition = $this->getPluginDefinition(); + return $plugin_definition['path'] . '/' . $this->getPluginId() . '/add'; + } + +} diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php index 7a50351..34daaca 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php +++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php @@ -1,7 +1,6 @@ alterInfo($module_handler, 'block'); $this->setCacheBackend($cache_backend, $language_manager, 'block_plugins'); } - - /** - * {@inheritdoc} - */ - public function processDefinition(&$definition, $plugin_id) { - parent::processDefinition($definition, $plugin_id); - - // Ensure that every block has a category. - $definition += array( - 'category' => $definition['provider'], - ); - } - } diff --git a/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php b/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php index de32cd1..deea1f4 100644 --- a/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php +++ b/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php @@ -1,5 +1,4 @@ pluginManager = $plugin_manager; + } + + /** * Implements EventSubscriberInterface::getSubscribedEvents(). */ public static function getSubscribedEvents() { @@ -37,32 +55,18 @@ public static function getSubscribedEvents() { */ public function routes(RouteBuildEvent $event) { $collection = $event->getRouteCollection(); - foreach (list_themes(TRUE) as $key => $theme) { - // The block entity listing page. - $route = new Route( - "admin/structure/block/list/$key", - array( + foreach ($this->pluginManager->getDefinitions() as $plugin_id => $plugin) { + list($plugin_base, $key) = explode(':', $plugin_id); + if ($plugin_base == 'block_plugin_ui') { + $route = new Route('admin/structure/block/list/' . $plugin_id, array( '_controller' => '\Drupal\block\Controller\BlockListController::listing', + 'entity_type' => 'block', 'theme' => $key, - ), - array( + ), array( '_block_themes_access' => 'TRUE', - ) - ); - $collection->add("block_admin_display.$key", $route); - - // The block plugin listing page. - $route = new Route( - "admin/structure/block/list/$key/add/{category}", - array( - '_form' => '\Drupal\block\Form\PlaceBlocksForm', - 'category' => NULL, - 'theme' => $key, - ), - array('_block_themes_access' => 'TRUE') - ); - $collection->add("block_plugin_ui.$key", $route); + )); + $collection->add('block_admin_display.' . $plugin_id, $route); + } } } - } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php index c3a220f..8c64ac8 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php @@ -38,14 +38,14 @@ function testAdminTheme() { $this->drupalLogin($admin_user); // Ensure that access to block admin page is denied when theme is disabled. - $this->drupalGet('admin/structure/block/list/bartik'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:bartik'); $this->assertResponse(403); // Enable admin theme and confirm that tab is accessible. theme_enable(array('bartik')); $edit['admin_theme'] = 'bartik'; $this->drupalPost('admin/appearance', $edit, t('Save configuration')); - $this->drupalGet('admin/structure/block/list/bartik'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:bartik'); $this->assertResponse(200); } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php index 8dcfeaa..22f47e8 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php @@ -49,11 +49,11 @@ protected function setUp() { */ function testBlockLibrarySearch() { // Check that the block plugin is valid. - $this->drupalPost('admin/structure/block/list/stark/add', array('block' => 'invalid_block'), t('Next')); + $this->drupalPost('admin/structure/block/list/block_plugin_ui:stark/add', array('block' => 'invalid_block'), t('Next')); $this->assertText('You must select a valid block.'); // Check that the block search form redirects to the correct block form. - $this->drupalPost('admin/structure/block/list/stark/add', array('block' => 'system_main_block'), t('Next')); + $this->drupalPost('admin/structure/block/list/block_plugin_ui:stark/add', array('block' => 'system_main_block'), t('Next')); $this->assertUrl('admin/structure/block/add/system_main_block/stark'); } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php index c766053..e665609 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php @@ -274,7 +274,7 @@ function testBlockModuleDisable() { } // Ensure that the disabled module's block plugin is no longer available. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertNoText(t('Test block caching')); // Confirm that the block is no longer displayed on the front page. diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php index 3036ecd..6562545 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php @@ -45,7 +45,7 @@ function testXSSInTitle() { $this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'access administration pages'))); $default_theme = config('system.theme')->get('default'); - $this->drupalGet('admin/structure/block/list/' . $default_theme . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . $default_theme . '/add'); $this->assertNoRaw("", 'The block title was properly sanitized in Block Plugin UI Admin page.'); } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockUiTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockUiTest.php index 62d2498..7ad84e1 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockUiTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockUiTest.php @@ -110,7 +110,7 @@ function testBlockAdminUiPage() { */ function testBlockSearch() { $block = t('Administration'); - $blocks = drupal_json_decode($this->drupalGet('block/autocomplete', array('query' => array('q' => $block)))); + $blocks = drupal_json_decode($this->drupalGet('system/autocomplete/block_plugin_ui:stark', array('query' => array('q' => $block)))); $this->assertEqual($blocks['system_menu_block:menu-admin'], $block, t('Can search for block with name !block.', array('!block' => $block))); } diff --git a/core/modules/block/lib/Drupal/block/Tests/NonDefaultBlockAdminTest.php b/core/modules/block/lib/Drupal/block/Tests/NonDefaultBlockAdminTest.php index 72f3d1d..7cf114c 100644 --- a/core/modules/block/lib/Drupal/block/Tests/NonDefaultBlockAdminTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/NonDefaultBlockAdminTest.php @@ -34,7 +34,7 @@ function testNonDefaultBlockAdmin() { $this->drupalLogin($admin_user); $new_theme = 'bartik'; theme_enable(array($new_theme)); - $this->drupalGet('admin/structure/block/list/' . $new_theme); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . $new_theme); $this->assertText('Bartik(' . t('active tab') . ')', 'Tab for non-default theme found.'); } } diff --git a/core/modules/breakpoint/breakpoint.install b/core/modules/breakpoint/breakpoint.install index dcc0b35..a419a9b 100644 --- a/core/modules/breakpoint/breakpoint.install +++ b/core/modules/breakpoint/breakpoint.install @@ -9,6 +9,8 @@ * Implements hook_enable(). * * Import breakpoints from all enabled themes. + * + * @todo: This should be removed if https://drupal.org/node/1813100 is resolved. */ function breakpoint_enable() { // Import breakpoints from themes. diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module index 2b819af..ce3c894 100644 --- a/core/modules/breakpoint/breakpoint.module +++ b/core/modules/breakpoint/breakpoint.module @@ -188,7 +188,7 @@ function _breakpoint_import_breakpoint_groups($source, $source_type) { */ function _breakpoint_delete_breakpoints($list, $source_type) { $ids = config_get_storage_names_with_prefix('breakpoint.breakpoint_group.' . $source_type . '.'); - $entity_info = Drupal::service('plugin.manager.entity')->getDefinition('breakpoint_group'); + $entity_info = entity_get_info('breakpoint_group'); // Remove the breakpoint.breakpoint part of the breakpoint identifier. foreach ($ids as &$id) { @@ -202,8 +202,8 @@ function _breakpoint_delete_breakpoints($list, $source_type) { $breakpoint_group->delete(); // Get all breakpoints defined by this theme/module. - $breakpoint_ids = Drupal::service('config.storage')->listAll('breakpoint.breakpoint.' . $source_type . '.' . $breakpoint_group->id() . '.'); - $entity_info = Drupal::service('plugin.manager.entity')->getDefinition('breakpoint'); + $breakpoint_ids = drupal_container()->get('config.storage')->listAll('breakpoint.breakpoint.' . $source_type . '.' . $breakpoint_group->id() . '.'); + $entity_info = entity_get_info('breakpoint'); // Remove the breakpoint.breakpoint part of the breakpoint identifier. foreach ($breakpoint_ids as &$breakpoint_id) { @@ -277,7 +277,7 @@ function breakpoint_get_theme_media_queries($theme_key) { * An array of breakpoints in the form $breakpoint['name'] = 'media query'. */ function breakpoint_get_module_media_queries($module) { - if (!Drupal::moduleHandler()->moduleExists($module)) { + if (!module_exists($module)) { throw new \Exception('Illegal module name passed.'); } diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php b/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php index 555eae3..da2bcfd 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php @@ -40,7 +40,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac } /** - * Retrieves enabled plugins' files, keyed by plugin ID. + * Determines which plug-ins are enabled. * * For CKEditor plugins that implement: * - CKEditorPluginButtonsInterface, not CKEditorPluginContextualInterface, @@ -64,7 +64,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac * the Drupal root-relative plugin files as values. * For internal plugins, the value is NULL. */ - public function getEnabledPluginFiles(Editor $editor, $include_internal_plugins = FALSE) { + public function getEnabledPlugins(Editor $editor, $include_internal_plugins = FALSE) { $plugins = array_keys($this->getDefinitions()); $toolbar_buttons = array_unique(NestedArray::mergeDeepArray($editor->settings['toolbar']['buttons'])); $enabled_plugins = array(); @@ -108,15 +108,16 @@ public function getEnabledPluginFiles(Editor $editor, $include_internal_plugins } /** - * Retrieves all available CKEditor buttons, keyed by plugin ID. + * Retrieves all plugins that implement CKEditorPluginButtonsInterface. * * @return array - * All availble CKEditor buttons, with plugin IDs as keys and button - * metadata (as implemented by getButtons()) as values. + * A list of the CKEditor plugins that implement buttons, with the plugin + * IDs as keys and lists of button metadata (as implemented by getButtons()) + * as values. * * @see CKEditorPluginButtonsInterface::getButtons() */ - public function getButtons() { + public function getButtonsPlugins() { $plugins = array_keys($this->getDefinitions()); $buttons_plugins = array(); diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php index eca1381..5b79bf2 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php @@ -86,7 +86,7 @@ public function settingsForm(array $form, array &$form_state, EditorEntity $edit $ckeditor_settings_toolbar = array( '#theme' => 'ckeditor_settings_toolbar', '#editor' => $editor, - '#plugins' => $this->ckeditorPluginManager->getButtons(), + '#plugins' => $this->ckeditorPluginManager->getButtonsPlugins(), ); $form['toolbar'] = array( '#type' => 'container', @@ -135,7 +135,7 @@ public function settingsForm(array $form, array &$form_state, EditorEntity $edit } } // Get a list of all buttons that are provided by all plugins. - $all_buttons = array_reduce($this->ckeditorPluginManager->getButtons(), function($result, $item) { + $all_buttons = array_reduce($this->ckeditorPluginManager->getButtonsPlugins(), function($result, $item) { return array_merge($result, array_keys($item)); }, array()); // Build a fake Editor object, which we'll use to generate JavaScript @@ -196,18 +196,18 @@ public function getJSSettings(EditorEntity $editor) { $settings = array(); // Get the settings for all enabled plugins, even the internal ones. - $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPluginFiles($editor, TRUE)); + $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPlugins($editor, TRUE)); foreach ($enabled_plugins as $plugin_id) { $plugin = $this->ckeditorPluginManager->createInstance($plugin_id); $settings += $plugin->getConfig($editor); } // Next, set the most fundamental CKEditor settings. - $external_plugin_files = $this->ckeditorPluginManager->getEnabledPluginFiles($editor); + $external_plugins = $this->ckeditorPluginManager->getEnabledPlugins($editor); $settings += array( 'toolbar' => $this->buildToolbarJSSetting($editor), 'contentsCss' => $this->buildContentsCssJSSetting($editor), - 'extraPlugins' => implode(',', array_keys($external_plugin_files)), + 'extraPlugins' => implode(',', array_keys($external_plugins)), // @todo: Remove image and link plugins from CKEditor build. 'removePlugins' => 'image,link', 'language' => $language_interface->id, @@ -221,7 +221,7 @@ public function getJSSettings(EditorEntity $editor) { // Finally, set Drupal-specific CKEditor settings. $settings += array( - 'drupalExternalPlugins' => array_map('file_create_url', $external_plugin_files), + 'drupalExternalPlugins' => array_map('file_create_url', $external_plugins), ); ksort($settings); @@ -238,7 +238,7 @@ public function getLibraries(EditorEntity $editor) { ); // Get the required libraries for any enabled plugins. - $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPluginFiles($editor)); + $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPlugins($editor)); foreach ($enabled_plugins as $plugin_id) { $plugin = $this->ckeditorPluginManager->createInstance($plugin_id); $additional_libraries = array_udiff($plugin->getLibraries($editor), $libraries, function($a, $b) { diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorPluginManagerTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorPluginManagerTest.php index cee1e75..3d31c04 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorPluginManagerTest.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorPluginManagerTest.php @@ -74,8 +74,8 @@ function testEnabledPlugins() { 'drupalimage' => 'core/modules/ckeditor/js/plugins/drupalimage/plugin.js', 'drupallink' => 'core/modules/ckeditor/js/plugins/drupallink/plugin.js', ); - $this->assertIdentical($enabled_plugins, $this->manager->getEnabledPluginFiles($editor), 'Only built-in plugins are enabled.'); - $this->assertIdentical(array('internal' => NULL) + $enabled_plugins, $this->manager->getEnabledPluginFiles($editor, TRUE), 'Only the "internal" plugin is enabled.'); + $this->assertIdentical($enabled_plugins, $this->manager->getEnabledPlugins($editor), 'Only built-in plugins are enabled.'); + $this->assertIdentical(array('internal' => NULL) + $enabled_plugins, $this->manager->getEnabledPlugins($editor, TRUE), 'Only the "internal" plugin is enabled.'); // Enable the CKEditor Test module, which has the Llama plugin (plus three // variations of it, to cover all possible ways a plugin can be enabled) and @@ -87,8 +87,8 @@ function testEnabledPlugins() { $plugin_ids = array_keys($this->manager->getDefinitions()); sort($plugin_ids); $this->assertIdentical(array('drupalimage', 'drupallink', 'internal', 'llama', 'llama_button', 'llama_contextual', 'llama_contextual_and_button', 'stylescombo'), $plugin_ids, 'Additional CKEditor plugins found.'); - $this->assertIdentical($enabled_plugins, $this->manager->getEnabledPluginFiles($editor), 'Only the internal plugins are enabled.'); - $this->assertIdentical(array('internal' => NULL) + $enabled_plugins, $this->manager->getEnabledPluginFiles($editor, TRUE), 'Only the "internal" plugin is enabled.'); + $this->assertIdentical($enabled_plugins, $this->manager->getEnabledPlugins($editor), 'Only the internal plugins are enabled.'); + $this->assertIdentical(array('internal' => NULL) + $enabled_plugins, $this->manager->getEnabledPlugins($editor, TRUE), 'Only the "internal" plugin is enabled.'); // Case 3: enable each of the newly available plugins, if possible: // a. Llama: cannot be enabled, since it does not implement @@ -111,19 +111,19 @@ function testEnabledPlugins() { $file['c'] = 'core/modules/ckeditor/tests/modules/js/llama_contextual.js'; $file['cb'] = 'core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js'; $expected = $enabled_plugins + array('llama_button' => $file['b'], 'llama_contextual_and_button' => $file['cb']); - $this->assertIdentical($expected, $this->manager->getEnabledPluginFiles($editor), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.'); - $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical($expected, $this->manager->getEnabledPlugins($editor), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPlugins($editor, TRUE), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.'); $editor->settings['toolbar']['buttons'][0] = $original_toolbar; $editor->settings['toolbar']['buttons'][0][] = 'Strike'; $editor->save(); $expected = $enabled_plugins + array('llama_contextual' => $file['c'], 'llama_contextual_and_button' => $file['cb']); - $this->assertIdentical($expected, $this->manager->getEnabledPluginFiles($editor), 'The LLamaContextual and LlamaContextualAndButton plugins are enabled.'); - $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LlamaContextual and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical($expected, $this->manager->getEnabledPlugins($editor), 'The LLamaContextual and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPlugins($editor, TRUE), 'The LlamaContextual and LlamaContextualAndButton plugins are enabled.'); $editor->settings['toolbar']['buttons'][0][] = 'Llama'; $editor->save(); $expected = $enabled_plugins + array('llama_button' => $file['b'], 'llama_contextual' => $file['c'], 'llama_contextual_and_button' => $file['cb']); - $this->assertIdentical($expected, $this->manager->getEnabledPluginFiles($editor), 'The LlamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.'); - $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LLamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical($expected, $this->manager->getEnabledPlugins($editor), 'The LlamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.'); + $this->assertIdentical(array('internal' => NULL) + $expected, $this->manager->getEnabledPlugins($editor, TRUE), 'The LLamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.'); } } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php index f97d6cf..7917731 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php @@ -166,11 +166,11 @@ function testListUI() { $this->assertTitle('Test configuration | Drupal'); // Test for the table. - $element = $this->xpath('//div[@class="l-content"]//table'); + $element = $this->xpath('//div[@id="content"]//table'); $this->assertTrue($element, 'Configuration entity list table found.'); // Test the table header. - $elements = $this->xpath('//div[@class="l-content"]//table/thead/tr/th'); + $elements = $this->xpath('//div[@id="content"]//table/thead/tr/th'); $this->assertEqual(count($elements), 3, 'Correct number of table header cells found.'); // Test the contents of each th cell. @@ -180,7 +180,7 @@ function testListUI() { } // Check the number of table row cells. - $elements = $this->xpath('//div[@class="l-content"]//table/tbody/tr[@class="odd"]/td'); + $elements = $this->xpath('//div[@id="content"]//table/tbody/tr[@class="odd"]/td'); $this->assertEqual(count($elements), 3, 'Correct number of table row cells found.'); // Check the contents of each row cell. The first cell contains the label, diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 56d9508..bce6a02 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -19,7 +19,7 @@ function content_translation_help($path, $arg) { case 'admin/help#content_translation': $output = ''; $output .= '

    ' . t('About') . '

    '; - $output .= '

    ' . t('The Content Translation module allows you to create and manage translations for your Drupal site content. You can specify which elements need to be translated at the content-type level for content items and comments, at the vocabulary level for taxonomy terms, and at the site level for user accounts. Other modules may provide additional elements that can be translated. For more information, see the online handbook entry for Content Translation.', array('!url' => 'http://drupal.org/documentation/modules/translation_entity')) . '

    '; + $output .= '

    ' . t('The Content Translation module allows you to create and manage translations for your Drupal site content. You can specify which elements need to be translated at the content-type level for content items and comments, at the vocabulary level for taxonomy terms, and at the site level for user accounts. Other modules may provide additional elements that can be translated. For more information, see the online handbook entry for Content Translation.', array('!url' => 'http://drupal.org/documentation/modules/entity_translation')) . '

    '; $output .= '

    ' . t('Uses') . '

    '; $output .= '
    '; $output .= '
    ' . t('Enabling translation') . '
    '; diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php index c17bd06..919603e 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php @@ -63,7 +63,7 @@ function testSettingsUI() { 'settings[comment][comment_node_article][translatable]' => TRUE, ); $this->assertSettings('comment', 'comment_node_article', FALSE, $edit); - $xpath_err = '//div[contains(@class, "error")]'; + $xpath_err = '//div[@id="messages"]//div[contains(@class, "error")]'; $this->assertTrue($this->xpath($xpath_err), 'Enabling translation only for entity bundles generates a form error.'); // Test that the translation settings are not stored if a non-configurable diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php index 74b779b..a83be54 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php @@ -190,7 +190,7 @@ protected function assertAuthoringInfo() { 'content_translation[created]' => '19/11/1978', ); $this->drupalPost($path, $edit, $this->getFormSubmitAction($entity)); - $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.'); + $this->assertTrue($this->xpath('//div[@id="messages"]//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.'); $this->assertEqual($entity->translation[$langcode]['uid'] == $values[$langcode]['uid'], 'Translation author correctly kept.'); $this->assertEqual($entity->translation[$langcode]['created'] == $values[$langcode]['created'], 'Translation date correctly kept.'); } diff --git a/core/modules/datetime/datetime.install b/core/modules/datetime/datetime.install new file mode 100644 index 0000000..e0362a8 --- /dev/null +++ b/core/modules/datetime/datetime.install @@ -0,0 +1,23 @@ + 'The date value', + 'type' => 'varchar', + 'length' => 20, + 'not null' => FALSE, + ); + $indexes = array( + 'value' => array('value'), + ); + return array('columns' => $db_columns, 'indexes' => $indexes); +} diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module index ebbcdd8..fdee1a6 100644 --- a/core/modules/datetime/datetime.module +++ b/core/modules/datetime/datetime.module @@ -91,6 +91,74 @@ function datetime_theme() { } /** + * Implements hook_field_is_empty(). + */ +function datetime_field_is_empty($item, $field_type) { + if (empty($item['value'])) { + return TRUE; + } + return FALSE; +} + +/** + * Implements hook_field_info(). + */ +function datetime_field_info() { + return array( + 'datetime' => array( + 'label' => 'Date', + 'description' => t('Create and store date values.'), + 'settings' => array( + 'datetime_type' => 'datetime', + ), + 'instance_settings' => array( + 'default_value' => 'now', + ), + 'default_widget' => 'datetime_default', + 'default_formatter' => 'datetime_default', + 'class' => '\Drupal\datetime\Type\DateTimeItem', + ), + ); +} + +/** + * Implements hook_field_settings_form(). + */ +function datetime_field_settings_form($field, $instance) { + $settings = $field['settings']; + + $form['datetime_type'] = array( + '#type' => 'select', + '#title' => t('Date type'), + '#description' => t('Choose the type of date to create.'), + '#default_value' => $settings['datetime_type'], + '#options' => array( + 'datetime' => t('Date and time'), + 'date' => t('Date only'), + ), + ); + return $form; +} + +/** + * Implements hook_field_instance_settings_form(). + */ +function datetime_field_instance_settings_form($field, $instance) { + $settings = $instance['settings']; + + $form['default_value'] = array( + '#type' => 'select', + '#title' => t('Default date'), + '#description' => t('Set a default value for this date.'), + '#default_value' => $settings['default_value'], + '#options' => array('blank' => t('No default value'), 'now' => t('The current date')), + '#weight' => 1, + ); + + return $form; +} + +/** * Validation callback for the datetime widget element. * * The date has already been validated by the datetime form type validator and @@ -167,6 +235,30 @@ function datetime_datelist_widget_validate(&$element, &$form_state) { } /** + * Implements hook_field_load(). + * + * The function generates a Date object for each field early so that it is + * cached in the field cache. This avoids the need to generate the object later. + * The date will be retrieved in UTC, the local timezone adjustment must be made + * in real time, based on the preferences of the site and user. + */ +function datetime_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) { + foreach ($entities as $id => $entity) { + foreach ($items[$id] as $delta => $item) { + $items[$id][$delta]['date'] = NULL; + $value = isset($item['value']) ? $item['value'] : NULL; + if (!empty($value)) { + $storage_format = $field['settings']['datetime_type'] == 'date' ? DATETIME_DATE_STORAGE_FORMAT: DATETIME_DATETIME_STORAGE_FORMAT; + $date = new DrupalDateTime($value, DATETIME_STORAGE_TIMEZONE, $storage_format); + if ($date instanceOf DrupalDateTime && !$date->hasErrors()) { + $items[$id][$delta]['date'] = $date; + } + } + } + } +} + +/** * Sets a default value for an empty date field. * * Callback for $instance['default_value_function'], as implemented by diff --git a/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php deleted file mode 100644 index 0c51dd4..0000000 --- a/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php +++ /dev/null @@ -1,140 +0,0 @@ - 'datetime_iso8601', - 'label' => t('Date value'), - ); - } - - return static::$propertyDefinitions; - } - - /** - * {@inheritdoc} - */ - public static function schema(FieldInterface $field) { - return array( - 'columns' => array( - 'value' => array( - 'description' => 'The date value.', - 'type' => 'varchar', - 'length' => 20, - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'value' => array('value'), - ), - ); - } - - /** - * {@inheritdoc} - */ - public function settingsForm(array $form, array &$form_state) { - $element = array(); - - $element['datetime_type'] = array( - '#type' => 'select', - '#title' => t('Date type'), - '#description' => t('Choose the type of date to create.'), - '#default_value' => $this->getFieldSetting('datetime_type'), - '#options' => array( - 'datetime' => t('Date and time'), - 'date' => t('Date only'), - ), - ); - - return $element; - } - - /** - * {@inheritdoc} - */ - public function instanceSettingsForm(array $form, array &$form_state) { - $element = array(); - - $element['default_value'] = array( - '#type' => 'select', - '#title' => t('Default date'), - '#description' => t('Set a default value for this date.'), - '#default_value' => $this->getFieldSetting('default_value'), - '#options' => array('blank' => t('No default value'), 'now' => t('The current date')), - '#weight' => 1, - ); - - return $element; - } - - /** - * {@inheritdoc} - */ - public function prepareCache() { - // The function generates a Date object for each field early so that it is - // cached in the field cache. This avoids the need to generate the object - // later. The date will be retrieved in UTC, the local timezone adjustment - // must be made in real time, based on the preferences of the site and user. - $value = $this->get('value')->getValue(); - if (!empty($value)) { - $storage_format = $this->getFieldSetting('datetime_type') == 'date' ? DATETIME_DATE_STORAGE_FORMAT : DATETIME_DATETIME_STORAGE_FORMAT; - $date = new DrupalDateTime($value, DATETIME_STORAGE_TIMEZONE, $storage_format); - if ($date instanceOf DrupalDateTime && !$date->hasErrors()) { - $this->set('date', $date); - } - } - } - - /** - * {@inheritdoc} - */ - public function isEmpty() { - $value = $this->get('value')->getValue(); - return $value === NULL || $value === ''; - } - -} diff --git a/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php new file mode 100644 index 0000000..78c1824 --- /dev/null +++ b/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php @@ -0,0 +1,39 @@ + 'datetime_iso8601', + 'label' => t('Date value'), + ); + } + return self::$propertyDefinitions; + } +} diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js index 18493d6..5893e11 100644 --- a/core/modules/editor/js/editor.js +++ b/core/modules/editor/js/editor.js @@ -60,10 +60,7 @@ Drupal.behaviors.editor = { if (event.isDefaultPrevented()) { return; } - // Detach the current editor (if any). - if (settings.editor.formats[activeFormatID]) { - Drupal.editorDetach(field, settings.editor.formats[activeFormatID], 'serialize'); - } + Drupal.editorDetach(field, settings.editor.formats[activeFormatID], 'serialize'); }); }); }, diff --git a/core/modules/editor/lib/Drupal/editor/Form/EditorImageDialog.php b/core/modules/editor/lib/Drupal/editor/Form/EditorImageDialog.php index dcc78a5..0fa6249 100644 --- a/core/modules/editor/lib/Drupal/editor/Form/EditorImageDialog.php +++ b/core/modules/editor/lib/Drupal/editor/Form/EditorImageDialog.php @@ -122,9 +122,8 @@ public function submitForm(array &$form, array &$form_state) { if (form_get_errors()) { unset($form['#prefix'], $form['#suffix']); - $status_messages = array('#theme' => 'status_messages'); $output = drupal_render($form); - $output = '
    ' . drupal_render($status_messages) . $output . '
    '; + $output = '
    ' . theme('status_messages') . $output . '
    '; $response->addCommand(new HtmlCommand('#editor-image-dialog-form', $output)); } else { diff --git a/core/modules/editor/lib/Drupal/editor/Form/EditorLinkDialog.php b/core/modules/editor/lib/Drupal/editor/Form/EditorLinkDialog.php index 3be9c82..4e17831 100644 --- a/core/modules/editor/lib/Drupal/editor/Form/EditorLinkDialog.php +++ b/core/modules/editor/lib/Drupal/editor/Form/EditorLinkDialog.php @@ -89,9 +89,8 @@ public function submitForm(array &$form, array &$form_state) { if (form_get_errors()) { unset($form['#prefix'], $form['#suffix']); - $status_messages = array('#theme' => 'status_messages'); $output = drupal_render($form); - $output = '
    ' . drupal_render($status_messages) . $output . '
    '; + $output = '
    ' . theme('status_messages') . $output . '
    '; $response->addCommand(new HtmlCommand('#editor-link-dialog-form', $output)); } else { diff --git a/core/modules/email/lib/Drupal/email/Plugin/field/field_type/ConfigurableEmailItem.php b/core/modules/email/lib/Drupal/email/Plugin/field/field_type/ConfigurableEmailItem.php index b346c4b..d107238 100644 --- a/core/modules/email/lib/Drupal/email/Plugin/field/field_type/ConfigurableEmailItem.php +++ b/core/modules/email/lib/Drupal/email/Plugin/field/field_type/ConfigurableEmailItem.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\Plugin\DataType\EmailItem; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'email' field type. @@ -40,7 +40,7 @@ class ConfigurableEmailItem extends EmailItem { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php index 80b69b6..c451fdb 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Plugin\DataType\EntityReferenceItem; use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemInterface; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * A common base class for configurable entity reference fields. @@ -93,7 +93,7 @@ public function getPropertyDefinitions() { * Copied from \Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem, * since we cannot extend it. */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { $definition = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field->type); $module = $definition['provider']; module_load_install($module); diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemInterface.php index f37afa0..394ec01 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemInterface.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemInterface.php @@ -8,7 +8,7 @@ namespace Drupal\field\Plugin\Type\FieldType; use Drupal\Core\Entity\Field\FieldItemInterface; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Interface definition for 'configurable field type' plugins. @@ -18,7 +18,7 @@ /** * Returns the field instance definition. * - * @var \Drupal\field\FieldInstanceInterface + * @var \Drupal\field\Plugin\Core\Entity\FieldInstance */ public function getInstance(); @@ -29,7 +29,7 @@ public function getInstance(); * creation of the field. No field instances exist by then, and it is not * possible to instantiate a FieldItemInterface object yet. * - * @param \Drupal\field\FieldInterface $field + * @param \Drupal\field\Plugin\Core\Entity\Field $field * The field definition. * * @return array @@ -54,7 +54,7 @@ public function getInstance(); * specify another field as related, only existing SQL tables, * such as {taxonomy_term_data}. */ - public static function schema(FieldInterface $field); + public static function schema(Field $field); /** * Returns a form for the field-level settings. diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php index 0bad0d0..f5ab447 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php +++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Field\PrepareCacheInterface; use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation for legacy field types. @@ -29,7 +29,7 @@ /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { $definition = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field->type); $module = $definition['provider']; module_load_install($module); diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php index 621c08d..d4c392c 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php @@ -9,7 +9,6 @@ use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\Field\FieldTypePluginManager; -use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\field_ui\OverviewBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\field\Plugin\Core\Entity\Field; @@ -27,26 +26,16 @@ class FieldOverview extends OverviewBase { protected $fieldTypeManager; /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** * Constructs a new FieldOverview. * * @param \Drupal\Core\Entity\EntityManager $entity_manager * The entity manager. * @param \Drupal\Core\Entity\Field\FieldTypePluginManager $field_type_manager * The field type manager - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler to invoke hooks on. */ - public function __construct(EntityManager $entity_manager, FieldTypePluginManager $field_type_manager, ModuleHandlerInterface $module_handler) { - parent::__construct($entity_manager); + public function __construct(EntityManager $entity_manager, FieldTypePluginManager $field_type_manager) { + $this->entityManager = $entity_manager; $this->fieldTypeManager = $field_type_manager; - $this->moduleHandler = $module_handler; } /** @@ -55,8 +44,7 @@ public function __construct(EntityManager $entity_manager, FieldTypePluginManage public static function create(ContainerInterface $container) { return new static( $container->get('plugin.manager.entity'), - $container->get('plugin.manager.entity.field.field_type'), - $container->get('module_handler') + $container->get('plugin.manager.entity.field.field_type') ); } @@ -154,8 +142,6 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, 'href' => "$admin_field_path/delete", 'attributes' => array('title' => t('Delete instance.')), ); - // Allow altering the operations on this entity listing. - $this->moduleHandler->alter('entity_operation', $links, $instance); $table[$name]['operations']['data'] = array( '#type' => 'operations', '#links' => $links, diff --git a/core/modules/file/file.install b/core/modules/file/file.install index 614b07c..16611c2 100644 --- a/core/modules/file/file.install +++ b/core/modules/file/file.install @@ -340,19 +340,3 @@ function file_update_8003() { } } } - -/* - * Convert the 'filesize' column in {file_managed} to a bigint. - */ -function file_update_8004() { - $spec = array( - 'description' => 'The size of the file in bytes.', - 'type' => 'int', - 'size' => 'big', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ); - db_change_field('file_managed', 'filesize', 'filesize', $spec); -} - diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 4621ddd..8c92381 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -1065,8 +1065,7 @@ function template_preprocess_forum_list(&$variables) { } $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics; } - $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => $forum->last_post); - $variables['forums'][$id]->last_reply = drupal_render($forum_submitted); + $variables['forums'][$id]->last_reply = theme('forum_submitted', array('topic' => $forum->last_post)); } $variables['pager'] = array( @@ -1128,13 +1127,8 @@ function template_preprocess_forum_topic_list(&$variables) { $variables['topics'][$id]->title = l($topic->title, 'node/' . $topic->id()); $variables['topics'][$id]->message = ''; } - $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => $topic); - $variables['topics'][$id]->created = drupal_render($forum_submitted); - $forum_submitted = array( - '#theme' => 'forum_submitted', - '#topic' => isset($topic->last_reply) ? $topic->last_reply : NULL, - ); - $variables['topics'][$id]->last_reply = drupal_render($forum_submitted); + $variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic)); + $variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL)); $variables['topics'][$id]->new_text = ''; $variables['topics'][$id]->new_url = ''; @@ -1212,11 +1206,7 @@ function template_preprocess_forum_icon(&$variables) { * - topic: The topic object. */ function template_preprocess_forum_submitted(&$variables) { - $variables['author'] = ''; - if (isset($variables['topic']->uid)) { - $username = array('#theme' => 'username', '#account' => user_load($variables['topic']->uid)); - $variables['author'] = drupal_render($username); - } + $variables['author'] = isset($variables['topic']->uid) ? theme('username', array('account' => user_load($variables['topic']->uid))) : ''; $variables['time'] = isset($variables['topic']->created) ? format_interval(REQUEST_TIME - $variables['topic']->created) : ''; } diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php index fe50a4d..0171fb2 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php @@ -573,17 +573,13 @@ private function verifyForums($node_user, EntityInterface $node, $admin, $respon $this->drupalGet('node/' . $node->id()); $this->assertResponse(200); $this->assertTitle($node->label() . ' | Drupal', 'Forum node was displayed'); - $breadcrumb_build = array( + $breadcrumb = array( l(t('Home'), NULL), l(t('Forums'), 'forum'), l($this->forumContainer['name'], 'forum/' . $this->forumContainer['tid']), l($this->forum['name'], 'forum/' . $this->forum['tid']), ); - $breadcrumb = array( - '#theme' => 'breadcrumb', - '#breadcrumb' => $breadcrumb_build, - ); - $this->assertRaw(drupal_render($breadcrumb), 'Breadcrumbs were displayed'); + $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed'); // View forum edit node. $this->drupalGet('node/' . $node->id() . '/edit'); @@ -632,19 +628,15 @@ private function verifyForumView($forum, $parent = NULL) { $this->assertResponse(200); $this->assertTitle($forum['name'] . ' | Drupal'); - $breadcrumb_build = array( + $breadcrumb = array( l(t('Home'), NULL), l(t('Forums'), 'forum'), ); if (isset($parent)) { - $breadcrumb_build[] = l($parent['name'], 'forum/' . $parent['tid']); + $breadcrumb[] = l($parent['name'], 'forum/' . $parent['tid']); } - $breadcrumb = array( - '#theme' => 'breadcrumb', - '#breadcrumb' => $breadcrumb_build, - ); - $this->assertRaw(drupal_render($breadcrumb), 'Breadcrumbs were displayed'); + $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb))); } /** diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php index d6dc853..555c198 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php @@ -423,7 +423,7 @@ function testUrlLanguageFallback() { $this->assertTrue($fields[0] == $languages[$langcode_browser_fallback]->name, 'The browser language is the URL active language'); // Check that URLs are rewritten using the given browser language. - $fields = $this->xpath('//strong[@class="site-name"]/a[@rel="home" and @href=:url]', $args); + $fields = $this->xpath('//p[@id="site-name"]/strong/a[@rel="home" and @href=:url]', $args); $this->assertTrue($fields[0] == 'Drupal', 'URLs are rewritten using the browser language.'); } diff --git a/core/modules/link/lib/Drupal/link/Plugin/field/field_type/LinkItem.php b/core/modules/link/lib/Drupal/link/Plugin/field/field_type/LinkItem.php index 1fd42df..998e60f 100644 --- a/core/modules/link/lib/Drupal/link/Plugin/field/field_type/LinkItem.php +++ b/core/modules/link/lib/Drupal/link/Plugin/field/field_type/LinkItem.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'link' field type. @@ -59,7 +59,7 @@ public function getPropertyDefinitions() { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'url' => array( @@ -94,7 +94,7 @@ public function instanceSettingsForm(array $form, array &$form_state) { $element['title'] = array( '#type' => 'radios', '#title' => t('Allow link text'), - '#default_value' => $this->getFieldSetting('title'), + '#default_value' => $this->getFieldDefinition()->getFieldSetting('title'), '#options' => array( DRUPAL_DISABLED => t('Disabled'), DRUPAL_OPTIONAL => t('Optional'), diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php index 15a1c55..a03f1df 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php @@ -98,11 +98,15 @@ function testGetPluralFormat() { 1 => 0, 0 => 1, 5 => 1, + 123 => 1, + 235 => 1, ), 'fr' => array( 1 => 0, 0 => 0, 5 => 1, + 123 => 1, + 235 => 1, ), 'hr' => array( 1 => 0, @@ -110,6 +114,8 @@ function testGetPluralFormat() { 0 => 2, 2 => 1, 8 => 2, + 123 => 1, + 235 => 2, ), 'hu' => array( 1 => -1, diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateCronTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateCronTest.php index 219b7f0..04dd4fd 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateCronTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateCronTest.php @@ -108,8 +108,6 @@ function testUpdateCron() { $queue = \Drupal::queue('locale_translation', TRUE); $this->assertEqual($queue->numberOfItems(), 3, 'Queue holds tasks for one project.'); - // Ensure last checked is updated to a greater time than the initial value. - sleep(1); // Test: Execute cron and check if tasks are executed correctly. // Run cron to process the tasks in the queue. $this->drupalGet('admin/reports/status/run-cron'); diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index c83ff19..932b2a1 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -363,9 +363,12 @@ function locale_get_plural($count, $langcode = NULL) { // $count and statically cache the result for the combination of language // and count, since the result will always be identical. if (!empty($plural_formulas[$langcode])) { - // $n is used inside the expression in the eval(). - $n = $count; - $plural_indexes[$langcode][$count] = @eval('return intval(' . $plural_formulas[$langcode]['formula'] . ');'); + // Plural formulas are stored as an array for 0-199. 100 is the highest + // modulo used but storing 0-99 is not enough because below 100 we often + // find exceptions (1, 2, etc). + $index = $count > 199 ? 100 + ($count % 100) : $count; + $plural_indexes[$langcode][$count] = isset($plural_formulas[$langcode]['formula'][$index]) ? $plural_formulas[$langcode]['formula'][$index] : $plural_formulas[$langcode]['formula']['default']; + } // In case there is no plural formula for English (no imported translation // for English), use a default formula. diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php index 0ae93d4..1303f43 100644 --- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php +++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php @@ -161,7 +161,7 @@ function addCustomMenu() { // Enable the custom menu block. $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'. // Confirm that the custom menu block is available. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertText($label); // Enable the block. @@ -379,7 +379,7 @@ function testSystemMenuRename() { // Make sure menu shows up with new name in block addition. $default_theme = variable_get('theme_default', 'stark'); - $this->drupalget('admin/structure/block/list/' . $default_theme . '/add'); + $this->drupalget('admin/structure/block/list/block_plugin_ui:' . $default_theme . '/add'); $this->assertText($edit['label']); } diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php index 4ae0bef..68441f6 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php @@ -7,8 +7,8 @@ namespace Drupal\node\Plugin\views\field; -use Drupal\views\Plugin\views\field\ActionBulkForm; use Drupal\Component\Annotation\PluginID; +use Drupal\system\Plugin\views\field\BulkFormBase; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityManager; @@ -17,7 +17,7 @@ * * @PluginID("node_bulk_form") */ -class NodeBulkForm extends ActionBulkForm { +class NodeBulkForm extends BulkFormBase { /** * Constructs a new NodeBulkForm object. diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/BulkFormTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/BulkFormTest.php index 09f9c93..edcf2b1 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Views/BulkFormTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Views/BulkFormTest.php @@ -25,7 +25,7 @@ public static function getInfo() { return array( 'name' => 'Node: Bulk form', 'description' => 'Tests a node bulk form.', - 'group' => 'Views module integration', + 'group' => 'Views Modules', ); } diff --git a/core/modules/node/tests/Drupal/node/Tests/Plugin/views/field/NodeBulkFormTest.php b/core/modules/node/tests/Drupal/node/Tests/Plugin/views/field/NodeBulkFormTest.php deleted file mode 100644 index 56b750d..0000000 --- a/core/modules/node/tests/Drupal/node/Tests/Plugin/views/field/NodeBulkFormTest.php +++ /dev/null @@ -1,69 +0,0 @@ - 'Node: Bulk form', - 'description' => 'Tests the node bulk form plugin.', - 'group' => 'Views module integration', - ); - } - - /** - * Tests the constructor assignment of actions. - */ - public function testConstructor() { - $actions = array(); - - for ($i = 1; $i <= 2; $i++) { - $action = $this->getMockBuilder('Drupal\system\Plugin\Core\Entity\Action') - ->disableOriginalConstructor() - ->getMock(); - $action->expects($this->any()) - ->method('getType') - ->will($this->returnValue('node')); - $actions[$i] = $action; - } - - $action = $this->getMockBuilder('Drupal\system\Plugin\Core\Entity\Action') - ->disableOriginalConstructor() - ->getMock(); - $action->expects($this->any()) - ->method('getType') - ->will($this->returnValue('user')); - $actions[] = $action; - - $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager') - ->disableOriginalConstructor() - ->getMock(); - $storage_controller = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface'); - $storage_controller->expects($this->any()) - ->method('loadMultiple') - ->will($this->returnValue($actions)); - - $entity_manager->expects($this->any()) - ->method('getStorageController') - ->with('action') - ->will($this->returnValue($storage_controller)); - $node_bulk_form = new NodeBulkForm(array(), 'node_bulk_form', array(), $entity_manager); - - $this->assertAttributeEquals(array_slice($actions, 0, -1, TRUE), 'actions', $node_bulk_form); - } - -} diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php index d7ca60f..f6755e7 100644 --- a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php +++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; use Drupal\Component\Utility\MapArray; /** @@ -51,7 +51,7 @@ public function getPropertyDefinitions() { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php index 7a9850b..98bd97c 100644 --- a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php +++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'number_float' field type. @@ -46,7 +46,7 @@ public function getPropertyDefinitions() { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php index e7087bd..3c43df6 100644 --- a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php +++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'number_integer' field type. @@ -46,7 +46,7 @@ public function getPropertyDefinitions() { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/StandardProfileTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/StandardProfileTest.php index 62767a6..39f68cc 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/StandardProfileTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/StandardProfileTest.php @@ -231,6 +231,20 @@ protected function doFrontPageRdfaTests() { // Test properties that are displayed in both teaser and full mode. $this->assertRdfaArticleProperties($graph, "Teaser"); + // Title. + // @todo Once the title data is output consistently between full and teaser + // view modes, move this to _testCommonNodeProperties(). + $title = $this->article->get('title')->offsetGet(0)->get('value')->getValue(); + $expected_value = array( + 'type' => 'literal', + // The teaser title parses with additional whitespace. + 'value' => " + $title + ", + 'lang' => 'en', + ); + $this->assertTrue($graph->hasProperty($this->articleUri, 'http://schema.org/name', $expected_value), "Teaser title was found (schema:name)."); + // @todo Once the image points to the original instead of the processed // image, move this to testArticleProperties(). $image_file = file_load($this->article->get('field_image')->offsetGet(0)->get('target_id')->getValue()); @@ -265,6 +279,16 @@ protected function doArticleRdfaTests() { // Test the comment properties displayed on articles. $this->assertRdfaNodeCommentProperties($graph); + // Title. + // @todo Once the title data is output consistently between full and teaser + // view modes, move this to _testCommonNodeProperties(). + $expected_value = array( + 'type' => 'literal', + 'value' => $this->article->get('title')->offsetGet(0)->get('value')->getValue(), + 'lang' => 'en', + ); + $this->assertTrue($graph->hasProperty($this->articleUri, 'http://schema.org/name', $expected_value), "Article title was found (schema:name)."); + // @todo Once the image points to the original instead of the processed // image, move this to testArticleProperties(). $expected_value = array( @@ -296,6 +320,16 @@ protected function doPageRdfaTests() { // Test the properties that are common between pages and articles. $this->assertRdfaCommonNodeProperties($graph, $this->page, "Page"); + + // Title. + // @todo Once the title data is output consistently between full and teaser + // view modes, move this to _testCommonNodeProperties(). + $expected_value = array( + 'type' => 'literal', + 'value' => $this->page->get('title')->offsetGet(0)->get('value')->getValue(), + 'lang' => 'en', + ); + $this->assertTrue($graph->hasProperty($this->pageUri, 'http://schema.org/name', $expected_value), "Page title was found (schema:name)."); } /** @@ -360,14 +394,6 @@ protected function assertRdfaCommonNodeProperties($graph, NodeInterface $node, $ $uri_info = $node->uri(); $uri = url($uri_info['path'], array('absolute' => TRUE)); - // Title. - $expected_value = array( - 'type' => 'literal', - 'value' => $node->get('title')->offsetGet(0)->get('value')->getValue(), - 'lang' => 'en', - ); - $this->assertTrue($graph->hasProperty($uri, 'http://schema.org/name', $expected_value), "$message_prefix title was found (schema:name)."); - // Created date. $expected_value = array( 'type' => 'literal', diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 06b25a0..3bce0ee 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -254,17 +254,28 @@ function rdf_preprocess_node(&$variables) { $variables['attributes']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url']; $variables['attributes']['typeof'] = empty($bundle_mapping['types']) ? NULL : $bundle_mapping['types']; - // Adds RDFa markup for the node title as metadata because wrapping the title - // with markup is not reliable and the title output is different depdending on - // the view mode (e.g. full vs. teaser). + // Adds RDFa markup to the title of the node. Because the RDFa markup is + // added to the

    tag which might contain HTML code, we specify an empty + // datatype to ensure the value of the title read by the RDFa parsers is a + // literal. $title_mapping = $mapping->getPreparedFieldMapping('title'); - if ($title_mapping) { - $title_attributes['property'] = empty($title_mapping['properties']) ? NULL : $title_mapping['properties']; - $title_attributes['content'] = $variables['label']; - $variables['title_suffix']['rdf_meta_title'] = array( - '#theme' => 'rdf_metadata', - '#metadata' => array($title_attributes), + $variables['title_attributes']['property'] = empty($title_mapping['properties']) ? NULL : $title_mapping['properties']; + $variables['title_attributes']['datatype'] = ''; + + // In full node mode, the title is not displayed by node.html.twig so it is + // added in the tag of the HTML page. + if ($variables['page']) { + $element = array( + '#tag' => 'meta', + '#attributes' => array( + 'content' => $variables['label'], + 'about' => $variables['node_url'], + ), ); + if (!empty($title_mapping['properties'])) { + $element['#attributes']['property'] = $title_mapping['properties']; + } + drupal_add_html_head($element, 'rdf_node_title'); } // Adds RDFa markup for the relation between the node and its author. diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php index b829c7c..e851352 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php @@ -8,51 +8,13 @@ namespace Drupal\rest\Plugin; use Drupal\Component\Plugin\PluginBase; -use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * Common base class for resource plugins. */ -abstract class ResourceBase extends PluginBase implements ContainerFactoryPluginInterface, ResourceInterface { - - /** - * The available serialization formats. - * - * @var array - */ - protected $serializerFormats = array(); - - /** - * Constructs a Drupal\rest\Plugin\ResourceBase object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param array $plugin_definition - * The plugin implementation definition. - * @param array $serializer_formats - * The available serialization formats. - */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, array $serializer_formats) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->serializerFormats = $serializer_formats; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->getParameter('serializer.formats') - ); - } +abstract class ResourceBase extends PluginBase implements ResourceInterface { /** * Implements ResourceInterface::permissions(). @@ -106,7 +68,8 @@ public function routes() { case 'HEAD': // Restrict GET and HEAD requests to the media type specified in the // HTTP Accept headers. - foreach ($this->serializerFormats as $format_name) { + $formats = drupal_container()->getParameter('serializer.formats'); + foreach ($formats as $format_name) { // Expose one route per available format. //$format_route = new Route($route->getPattern(), $route->getDefaults(), $route->getRequirements()); $format_route = clone $route; @@ -161,5 +124,4 @@ public function availableMethods() { } return $available; } - } diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php b/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php index 0ad6b0f..6020634 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php @@ -7,35 +7,33 @@ namespace Drupal\rest\Plugin\Type; -use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManager; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator; +use Drupal\Component\Plugin\Factory\ReflectionFactory; +use Drupal\Component\Plugin\PluginManagerBase; +use Drupal\Core\Plugin\Discovery\AlterDecorator; +use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; use Drupal\Core\Plugin\Discovery\CacheDecorator; /** * Manages discovery and instantiation of resource plugins. */ -class ResourcePluginManager extends DefaultPluginManager { +class ResourcePluginManager extends PluginManagerBase { /** - * Constructs a new \Drupal\rest\Plugin\Type\ResourcePluginManager object. + * Overrides Drupal\Component\Plugin\PluginManagerBase::__construct(). * * @param \Traversable $namespaces * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend - * Cache backend instance to use. - * @param \Drupal\Core\Language\LanguageManager $language_manager - * The language manager. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler to invoke the alter hook with. + * keyed by the corresponding namespace to look for plugin implementations, */ - public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { - parent::__construct('Plugin/rest/resource', $namespaces); + public function __construct(\Traversable $namespaces) { + // Create resource plugin derivatives from declaratively defined resources. + $this->discovery = new AnnotatedClassDiscovery('Plugin/rest/resource', $namespaces); + $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); + $this->discovery = new AlterDecorator($this->discovery, 'rest_resource'); + $this->discovery = new CacheDecorator($this->discovery, 'rest'); - $this->setCacheBackend($cache_backend, $language_manager, 'rest_plugins'); - $this->alterInfo($module_handler, 'rest_resource'); + $this->factory = new ReflectionFactory($this->discovery); } /** diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php index 33a7e6b..694a422 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php @@ -9,11 +9,8 @@ use Drupal\Component\Annotation\Plugin; use Drupal\Core\Annotation\Translation; -use Drupal\Core\ContentNegotiation; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\PathPluginBase; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\RouteCollection; @@ -73,59 +70,16 @@ class RestExport extends PathPluginBase { protected $mimeType; /** - * The content negotiation library. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $contentNegotiation; - - /** - * The request object. - * - * @var \Symfony\Component\HttpFoundation\Request - */ - protected $request; - - /** - * Constructs a Drupal\rest\Plugin\ResourceBase object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param array $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\ContentNegotiation $content_negotiation - * The content negotiation library. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, ContentNegotiation $content_negotiation, Request $request) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->contentNegotiation = $content_negotiation; - $this->request = $request; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('content_negotiation'), - $container->get('request') - ); - } - - /** * {@inheritdoc} */ public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) { parent::initDisplay($view, $display, $options); - $request_content_type = $this->contentNegotiation->getContentType($this->request); + $container = drupal_container(); + $negotiation = $container->get('content_negotiation'); + $request = $container->get('request'); + + $request_content_type = $negotiation->getContentType($request); // Only use the requested content type if it's not 'html'. If it is then // default to 'json' to aid debugging. // @todo Remove the need for this when we have better content negotiation. @@ -133,7 +87,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio $this->setContentType($request_content_type); } - $this->setMimeType($this->request->getMimeType($this->contentType)); + $this->setMimeType($request->getMimeType($this->contentType)); } /** diff --git a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php index 0a75cb3..00ca7e5 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php @@ -33,7 +33,7 @@ public static function getInfo() { * Tests several valid and invalid create requests on all entity types. */ public function testCreate() { - $serializer = $this->container->get('serializer'); + $serializer = drupal_container()->get('serializer'); $entity_types = array('entity_test', 'node'); foreach ($entity_types as $entity_type) { diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php index 7f54177..6ad9000 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php @@ -209,7 +209,7 @@ protected function enableService($resource_type, $method = 'GET', $format = NULL $config->save(); // Rebuild routing cache, so that the REST API paths are available. - $this->container->get('router.builder')->rebuild(); + drupal_container()->get('router.builder')->rebuild(); // Reset the Simpletest permission cache, so that the new resource // permissions get picked up. drupal_static_reset('checkPermissions'); diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php index b417f2a..0c17d6e 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php @@ -33,7 +33,7 @@ public static function getInfo() { * Tests several valid and invalid partial update requests on test entities. */ public function testPatchUpdate() { - $serializer = $this->container->get('serializer'); + $serializer = drupal_container()->get('serializer'); // @todo once EntityNG is implemented for other entity types test all other // entity types here as well. $entity_type = 'entity_test'; diff --git a/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php index 48bf955..332310e 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php @@ -109,7 +109,7 @@ public function testSerializerResponses() { $this->executeView($view); // Get the serializer service. - $serializer = $this->container->get('serializer'); + $serializer = drupal_container()->get('serializer'); $entities = array(); foreach ($view->result as $row) { diff --git a/core/modules/rest/rest.module b/core/modules/rest/rest.module index d0e0551..9fe2674 100644 --- a/core/modules/rest/rest.module +++ b/core/modules/rest/rest.module @@ -10,12 +10,14 @@ */ function rest_permission() { $permissions = array(); - $manager = Drupal::service('plugin.manager.rest'); - $resources = config('rest.settings')->get('resources'); - if ($resources && $enabled = array_intersect_key($manager->getDefinitions(), $resources)) { - foreach ($enabled as $key => $resource) { - $plugin = $manager->getInstance(array('id' => $key)); - $permissions = array_merge($permissions, $plugin->permissions()); + if (drupal_container()->has('plugin.manager.rest')) { + $manager = drupal_container()->get('plugin.manager.rest'); + $resources = config('rest.settings')->get('resources'); + if ($resources && $enabled = array_intersect_key($manager->getDefinitions(), $resources)) { + foreach ($enabled as $key => $resource) { + $plugin = $manager->getInstance(array('id' => $key)); + $permissions = array_merge($permissions, $plugin->permissions()); + } } } return $permissions; diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index d238442..97817ad 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -1,14 +1,7 @@ services: plugin.manager.rest: class: Drupal\rest\Plugin\Type\ResourcePluginManager - arguments: ['@container.namespaces', '@cache.rest', '@language_manager', '@module_handler'] - cache.rest: - class: Drupal\Core\Cache\CacheBackendInterface - tags: - - { name: cache.bin } - factory_method: get - factory_service: cache_factory - arguments: [rest] + arguments: ['@container.namespaces'] rest.route_subscriber: class: Drupal\rest\EventSubscriber\RouteSubscriber tags: diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php index 0e275f7..a67b53b 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php @@ -7,7 +7,10 @@ namespace Drupal\shortcut\Controller; -use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Routing\PathBasedGeneratorInterface; use Drupal\shortcut\ShortcutSetInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -17,7 +20,55 @@ /** * Builds the page for administering shortcut sets. */ -class ShortcutSetController extends ControllerBase { +class ShortcutSetController implements ControllerInterface { + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Stores the entity manager. + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** + * The URL generator. + * + * @var \Drupal\Core\Routing\PathBasedGeneratorInterface + */ + protected $urlGenerator; + + /** + * Constructs a new \Drupal\shortcut\Controller\ShortCutController object. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Routing\PathBasedGeneratorInterface $url_generator + * The URL generator. + */ + public function __construct(EntityManager $entity_manager, ModuleHandlerInterface $module_handler, PathBasedGeneratorInterface $url_generator) { + $this->entityManager = $entity_manager; + $this->moduleHandler = $module_handler; + $this->urlGenerator = $url_generator; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity'), + $container->get('module_handler'), + $container->get('url_generator') + ); + } /** * Creates a new link in the provided shortcut set. @@ -42,7 +93,7 @@ public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Reques 'link_title' => $title, 'link_path' => $link, ); - $this->moduleHandler()->loadInclude('shortcut', 'admin.inc'); + $this->moduleHandler->loadInclude('shortcut', 'admin.inc'); shortcut_admin_add_link($link, $shortcut_set); if ($shortcut_set->save() == SAVED_UPDATED) { drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title']))); @@ -50,7 +101,7 @@ public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Reques else { drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title']))); } - return new RedirectResponse($this->urlGenerator()->generateFromPath('', array('absolute' => TRUE))); + return new RedirectResponse($this->urlGenerator->generateFromPath('', array('absolute' => TRUE))); } throw new AccessDeniedHttpException(); diff --git a/core/modules/system/config/system.filter.yml b/core/modules/system/config/system.filter.yml index 12ce55a..3bfbd9a 100644 --- a/core/modules/system/config/system.filter.yml +++ b/core/modules/system/config/system.filter.yml @@ -4,7 +4,6 @@ protocols: - ftp - news - nntp - - tel - telnet - mailto - irc diff --git a/core/modules/system/lib/Drupal/system/Access/SystemPluginUiCheck.php b/core/modules/system/lib/Drupal/system/Access/SystemPluginUiCheck.php new file mode 100644 index 0000000..db13644 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Access/SystemPluginUiCheck.php @@ -0,0 +1,55 @@ +pluginUiManager = $plugin_ui_manager; + } + + /** + * {@inheritdoc} + */ + public function applies(Route $route) { + return array_key_exists('_access_system_plugin_ui', $route->getRequirements()); + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, Request $request) { + if ($request->attributes->get('plugin_id')) { + // Checks access for a given plugin using the plugin's access() method. + $plugin_ui = $this->pluginUiManager->createInstance($request->attributes->get('plugin_id'), array()); + return $plugin_ui->access(NULL) ? static::ALLOW : static::DENY; + } + } + +} diff --git a/core/modules/system/lib/Drupal/system/Controller/SystemController.php b/core/modules/system/lib/Drupal/system/Controller/SystemController.php new file mode 100644 index 0000000..bd71353 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Controller/SystemController.php @@ -0,0 +1,56 @@ +query->get('q'); + $string_typed = Tags::explode($string_typed); + $string = Unicode::strtolower(array_pop($string_typed)); + $matches = array(); + if ($string) { + $plugin_ui = $this->container->get('plugin.manager.system.plugin_ui')->getDefinition($plugin_id); + $manager = $this->container->get($plugin_ui['manager']); + $titles = array(); + foreach($manager->getDefinitions() as $plugin_id => $plugin) { + $titles[$plugin_id] = $plugin[$plugin_ui['title_attribute']]; + } + $matches = preg_grep("/\b". $string . "/i", $titles); + } + + return new JsonResponse($matches); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Controller/SystemInfoController.php b/core/modules/system/lib/Drupal/system/Controller/SystemInfoController.php index c34efad..af48c1f 100644 --- a/core/modules/system/lib/Drupal/system/Controller/SystemInfoController.php +++ b/core/modules/system/lib/Drupal/system/Controller/SystemInfoController.php @@ -53,7 +53,7 @@ public function __construct(SystemManager $systemManager) { public function status() { $requirements = $this->systemManager->listRequirements(); $this->systemManager->fixAnonymousUid(); - return array('#theme' => 'status_report', '#requirements' => $requirements); + return theme('status_report', array('requirements' => $requirements)); } /** diff --git a/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php new file mode 100644 index 0000000..9328a5c --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php @@ -0,0 +1,51 @@ +discovery = new AnnotatedClassDiscovery('Plugin/PluginUI', $namespaces); + $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); + $this->discovery = new AlterDecorator($this->discovery, 'plugin_ui'); + $this->discovery = new CacheDecorator($this->discovery, 'plugin_ui'); + $this->factory = new DefaultFactory($this->discovery); + } + + /** + * Overrides \Drupal\Component\Plugin\PluginManagerBase::processDefinition(). + */ + public function processDefinition(&$definition, $plugin_id) { + $definition += array( + 'default_task' => TRUE, + 'task_title' => t('View'), + 'task_suffix' => 'view', + 'access_callback' => 'user_access', + ); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php index 128dc30..783eab2 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php @@ -480,10 +480,9 @@ function testLibraryRender() { $this->assertTrue(strpos($styles, 'core/assets/vendor/farbtastic/farbtastic.css'), 'Stylesheet of library was added to the page.'); $result = drupal_add_library('common_test', 'shorthand.plugin'); - $path = drupal_get_path('module', 'common_test') . '/js/shorthand.js?v=0.8.3.37'; + $path = drupal_get_path('module', 'common_test') . '/js/shorthand.js'; $scripts = drupal_get_js(); $this->assertTrue(strpos($scripts, $path), 'JavaScript specified in hook_library_info() using shorthand format (without any options) was added to the page.'); - $this->assertEqual(substr_count($scripts, 'shorthand.js'), 1, 'Shorthand JavaScript file only added once.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php index c964577..ec115e3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php @@ -73,68 +73,6 @@ function testDrupalRenderBasics() { 'expected' => '', ), - // Test that #theme and #theme_wrappers can co-exist on an element. - array( - 'name' => '#theme and #theme_wrappers basic', - 'value' => array( - '#theme' => 'common_test_foo', - '#foo' => 'foo', - '#bar' => 'bar', - '#theme_wrappers' => array('container'), - '#attributes' => array('class' => 'baz'), - ), - 'expected' => '
    foobar
    ', - ), - // Test that #theme_wrappers can disambiguate element attributes shared - // with rendering methods that build #children by using the alternate - // #theme_wrappers attribute override syntax. - array( - 'name' => '#theme and #theme_wrappers attribute disambiguation', - 'value' => array( - '#type' => 'link', - '#theme_wrappers' => array( - 'container' => array( - '#attributes' => array('class' => 'baz'), - ), - ), - '#attributes' => array('id' => 'foo'), - '#href' => 'http://drupal.org', - '#title' => 'bar', - ), - 'expected' => '', - ), - // Test that #theme_wrappers can disambiguate element attributes when the - // "base" attribute is not set for #theme. - array( - 'name' => '#theme_wrappers attribute disambiguation with undefined #theme attribute', - 'value' => array( - '#type' => 'link', - '#href' => 'http://drupal.org', - '#title' => 'foo', - '#theme_wrappers' => array( - 'container' => array( - '#attributes' => array('class' => 'baz'), - ), - ), - ), - 'expected' => '', - ), - // Two 'container' #theme_wrappers, one using the "base" attributes and - // one using an override. - array( - 'name' => 'Two #theme_wrappers container hooks with different attributes', - 'value' => array( - '#attributes' => array('class' => 'foo'), - '#theme_wrappers' => array( - 'container' => array( - '#attributes' => array('class' => 'bar'), - ), - 'container', - ), - ), - 'expected' => '
    ', - ), - // Test handling of #markup as a fallback for #theme hooks. // Simple #markup with no theme. array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/DatabaseTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Database/DatabaseTestBase.php index 0aa4d5b..17d7ec0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/DatabaseTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/DatabaseTestBase.php @@ -24,7 +24,6 @@ function setUp() { $this->installSchema('database_test', array( 'test', 'test_people', - 'test_people_copy', 'test_one_blob', 'test_two_blobs', 'test_task', diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/InsertTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/InsertTest.php index 46f66fb..76afcf8 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/InsertTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/InsertTest.php @@ -142,9 +142,9 @@ function testInsertLastInsertID() { } /** - * Tests that the INSERT INTO ... SELECT (fields) ... syntax works. + * Tests that the INSERT INTO ... SELECT ... syntax works. */ - function testInsertSelectFields() { + function testInsertSelect() { $query = db_select('test_people', 'tp'); // The query builder will always append expressions after fields. // Add the expression first to test that the insert fields are correctly @@ -166,26 +166,4 @@ function testInsertSelectFields() { $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); } - - /** - * Tests that the INSERT INTO ... SELECT * ... syntax works. - */ - function testInsertSelectAll() { - $query = db_select('test_people', 'tp') - ->fields('tp') - ->condition('tp.name', 'Meredith'); - - // The resulting query should be equivalent to: - // INSERT INTO test_people_copy - // SELECT * - // FROM test_people tp - // WHERE tp.name = 'Meredith' - db_insert('test_people_copy') - ->from($query) - ->execute(); - - $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - } - } diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php index dd8920c..c059f44 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php @@ -115,10 +115,7 @@ function testThemeSettings() { // Verify the actual 'src' attribute of the logo being output. $this->drupalGet(''); - $elements = $this->xpath('//header/a[@rel=:rel]/img', array( - ':rel' => 'home', - ) - ); + $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo')); $this->assertEqual((string) $elements[0]['src'], $expected['src']); } $unsupported_paths = array( @@ -166,10 +163,7 @@ function testThemeSettings() { $uploaded_filename = 'public://' . $fields[0]['value']; $this->drupalGet(''); - $elements = $this->xpath('//header/a[@rel=:rel]/img', array( - ':rel' => 'home', - ) - ); + $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo')); $this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename)); } diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 05c67a7..aa36ba7 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -572,31 +572,33 @@ function theme_status_report($variables) { $output .= ''; foreach ($requirements as $requirement) { - // Always use the explicit requirement severity, if defined. Otherwise, - // default to REQUIREMENT_OK in the installer to visually confirm that - // installation requirements are met. And default to REQUIREMENT_INFO to - // denote neutral information without special visualization. - if (isset($requirement['severity'])) { - $severity = $severities[(int) $requirement['severity']]; - } - elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') { - $severity = $severities[REQUIREMENT_OK]; - } - else { - $severity = $severities[REQUIREMENT_INFO]; - } + if (empty($requirement['#type'])) { + // Always use the explicit requirement severity, if defined. Otherwise, + // default to REQUIREMENT_OK in the installer to visually confirm that + // installation requirements are met. And default to REQUIREMENT_INFO to + // denote neutral information without special visualization. + if (isset($requirement['severity'])) { + $severity = $severities[(int) $requirement['severity']]; + } + elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') { + $severity = $severities[REQUIREMENT_OK]; + } + else { + $severity = $severities[REQUIREMENT_INFO]; + } - $severity['icon'] = '
    ' . $severity['title'] . '
    '; + $severity['icon'] = '
    ' . $severity['title'] . '
    '; - // Output table rows. - $output .= ''; - $output .= '' . $severity['icon'] . ''; - $output .= '' . $requirement['title'] . ''; - $output .= '' . $requirement['value']; - if (!empty($requirement['description'])) { - $output .= '
    ' . $requirement['description'] . '
    '; + // Output table rows. + $output .= ''; + $output .= '' . $severity['icon'] . ''; + $output .= '' . $requirement['title'] . ''; + $output .= '' . $requirement['value']; + if (!empty($requirement['description'])) { + $output .= '
    ' . $requirement['description'] . '
    '; + } + $output .= ''; } - $output .= ''; } $output .= ''; diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 8ef7556..2b362a0 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -3602,35 +3602,3 @@ function hook_filetransfer_info_alter(&$filetransfer_info) { /** * @} End of "defgroup update_api". */ - -/** - * @defgroup annotation Annotations - * @{ - * Annotations for class discovery and metadata description. - * - * The Drupal plugin system has a set of reusable components that developers - * can use, override, and extend in their modules. Most of the plugins use - * annotations, which let classes register themselves as plugins and describe - * their metadata. (Annotations can also be used for other purposes, though - * at the moment, Drupal only uses them for the plugin system.) - * - * To annotate a class as a plugin, add code similar to the following to the - * end of the documentation block immediately preceding the class declaration: - * @code - * * @EntityType( - * * id = "comment", - * * label = @Translation("Comment"), - * * ... - * * base_table = "comment" - * * ) - * @endcode - * - * The available annotation classes are listed in this topic, and can be - * identified when you are looking at the Drupal source code by having - * "@ Annotation" in their documentation blocks (without the space after @). To - * find examples of annotation for a particular annotation class, such as - * EntityType, look for class files that contain a PHP "use" declaration of the - * annotation class, or files that have an @ annotation section using the - * annotation class. - * @} - */ diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 1935f5c..fc7bb6d 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -964,10 +964,84 @@ function system_menu() { ); } + foreach (drupal_container()->get('plugin.manager.system.plugin_ui')->getDefinitions() as $plugin_id => $plugin) { + if ($plugin['menu'] === TRUE) { + $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']] = array( + 'title' => $plugin['title'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array($plugin['id'], $plugin_id), + 'access callback' => 'system_plugin_ui_access', + 'access arguments' => array($plugin_id), + 'type' => $plugin['type'], + ); + if (!empty($plugin['default_task'])) { + $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix'] . '/' . $plugin['task_suffix']] = array( + 'title' => $plugin['task_title'], + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + } + $items[$plugin['path'] . '/' . $plugin_id . '/%'] = array( + 'title' => $plugin['title'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array($plugin['id'], $plugin_id, (count(explode('/', $plugin['path'])) + 1)), + 'access callback' => 'system_plugin_ui_access', + 'access arguments' => array($plugin_id, (count(explode('/', $plugin['path'])) + 1)), + 'type' => MENU_CALLBACK, + ); + if (!empty($plugin['file'])) { + $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']]['file'] = $plugin['file']; + $items[$plugin['path'] . '/' . $plugin_id . '/%']['file'] = $plugin['file']; + if (!empty($plugin['file_path'])) { + $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']]['file path'] = $plugin['file_path']; + $items[$plugin['path'] . '/' . $plugin_id . '/%']['file path'] = $plugin['file_path']; + } + } + } + } + return $items; } /** + * Proxies to the plugin class' form method. + * + * @todo This needs more explanation, an @see or two, and parameter + * documentation. Also "proxies" is a weird word to use. + */ +function system_plugin_ui_form($form, &$form_state, $plugin, $facet = NULL) { + $plugin_ui = drupal_container()->get('plugin.manager.system.plugin_ui')->createInstance($plugin); + $form = $plugin_ui->form($form, $form_state, $facet); + $form['#validate'][] = array($plugin_ui, 'formValidate'); + $form['#submit'][] = array($plugin_ui, 'formSubmit'); + return $form; +} + +/** + * Checks access for a given plugin using the plugin's access() method. + * + * @todo This needs more explanation, some @see, and parameter documentation. + * @todo What the heck kind of parameter name is "facet"? + */ +function system_plugin_ui_access($plugin, $facet = NULL) { + $plugin_ui = drupal_container()->get('plugin.manager.system.plugin_ui')->createInstance($plugin); + return $plugin_ui->access($facet); +} + +/** + * Implements hook_forms(). + */ +function system_forms() { + $forms = array(); + foreach (drupal_container()->get('plugin.manager.system.plugin_ui')->getDefinitions() as $plugin_id => $plugin) { + if (empty($forms[$plugin['id']])) { + $forms[$plugin['id']]['callback'] = 'system_plugin_ui_form'; + } + } + return $forms; +} + +/** * Theme callback for the default batch page. */ function _system_batch_theme() { diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 017d486..61363d6 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -213,3 +213,10 @@ system_timezone: _controller: '\Drupal\system\Controller\TimezoneController::getTimezone' requirements: _access: 'TRUE' + +system_plugin_autocomplete: + pattern: '/system/autocomplete/{plugin_id}' + defaults: + _controller: 'Drupal\system\Controller\SystemController::autocomplete' + requirements: + _access_system_plugin_ui: 'TRUE' diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 08d67ad..0c770d3 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -3,6 +3,14 @@ services: class: Drupal\system\Access\CronAccessCheck tags: - { name: access_check } + access_check.system_plugin_ui: + class: Drupal\system\Access\SystemPluginUiCheck + tags: + - { name: access_check } + arguments: ['@plugin.manager.system.plugin_ui'] + plugin.manager.system.plugin_ui: + class: Drupal\system\Plugin\Type\PluginUIManager + arguments: ['@container.namespaces'] system.manager: class: Drupal\system\SystemManager arguments: ['@module_handler', '@database'] diff --git a/core/modules/system/templates/authorize-message.html.twig b/core/modules/system/templates/authorize-message.html.twig new file mode 100644 index 0000000..4579f2d --- /dev/null +++ b/core/modules/system/templates/authorize-message.html.twig @@ -0,0 +1,20 @@ +{# +/** + * @file + * Default theme implementation for a log message in an authorize.php report. + * + * This template displays a single message in an authorize.php batch operation + * report. + * + * Available variables: + * - message: The log message. + * - success: A flag indicating failure or success. + * + * @ingroup themeable + */ +#} +{% if success %} + {{ message }} +{% else %} + {{ message }} +{% endif %} diff --git a/core/modules/system/templates/authorize-report.html.twig b/core/modules/system/templates/authorize-report.html.twig new file mode 100644 index 0000000..e3c5dbb --- /dev/null +++ b/core/modules/system/templates/authorize-report.html.twig @@ -0,0 +1,23 @@ +{# +/** + * @file + * Default theme implementation for authorize.php operation report templates. + * + * This report displays the results of an operation run via authorize.php. + * + * Available variables: + * - report: A list of result messages. + * - attributes: HTML attributes for the element. + * + * @see template_preprocess_authorize_report() + * + * @ingroup themeable + */ +#} +{% if report %} + + {% for messages in report %} + {{ messages }} + {% endfor %} + +{% endif %} diff --git a/core/modules/system/templates/dropbutton-wrapper.html.twig b/core/modules/system/templates/dropbutton-wrapper.html.twig deleted file mode 100644 index ca0ff7e..0000000 --- a/core/modules/system/templates/dropbutton-wrapper.html.twig +++ /dev/null @@ -1,23 +0,0 @@ -{# -/** - * @file - * Default theme implementation for a dropbutton wrapper. - * - * Available variables: - * - children: Contains the child elements of the dropbutton menu. - * - * @see template_preprocess() - * @see template_preprocess_dropbutton_wrapper() - * - * @ingroup themeable - */ -#} -{% if children %} - {% spaceless %} -
    -
    - {{ children }} -
    -
    - {% endspaceless %} -{% endif %} diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/maintenance-page.html.twig index cfa18a8..03255e9 100644 --- a/core/modules/system/templates/maintenance-page.html.twig +++ b/core/modules/system/templates/maintenance-page.html.twig @@ -12,69 +12,80 @@ * @ingroup themeable */ #} - - - {{ head }} + + + {{ head_title }} + {{ head }} {{ styles }} {{ scripts }} +
    + - {% if page.sidebar_first %} - {# /.l-sidebar-first #} - {% endif %} + {% if sidebar_second %} + + {% endif %} - {% if page.sidebar_second %} - {# /.l-sidebar-second #} - {% endif %} +
    - {% if page.footer %} -
    - {{ page.footer }} -
    - {% endif %} + -{# /.l-container #} + diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig index 1f6f916..cf7bafb 100644 --- a/core/modules/system/templates/page.html.twig +++ b/core/modules/system/templates/page.html.twig @@ -63,36 +63,39 @@ * @ingroup themeable */ #} -
    +
    + +
    {% endif %} - +
    - {% if page.footer %} -
    - {{ page.footer }} -
    - {% endif %} +
    + {{ page.footer }} +
    -{# /.l-container #} + diff --git a/core/modules/system/templates/progress-bar.html.twig b/core/modules/system/templates/progress-bar.html.twig deleted file mode 100644 index 0d11991..0000000 --- a/core/modules/system/templates/progress-bar.html.twig +++ /dev/null @@ -1,23 +0,0 @@ -{# -/** - * @file - * Default theme implementation for a progress bar. - * - * Note that the core Batch API uses this only for non-JavaScript batch jobs. - * - * Available variables: - * - label: The label of the working task. - * - percent: The percentage of the progress. - * - message: A string containing information to be displayed. - * - * @ingroup themeable - */ -#} -
    - {% if label %} -
    {{ label }}
    - {% endif %} -
    -
    {{ percent }}%
    -
    {{ message }}
    -
    diff --git a/core/modules/system/templates/task-list.html.twig b/core/modules/system/templates/task-list.html.twig new file mode 100644 index 0000000..4242dc2 --- /dev/null +++ b/core/modules/system/templates/task-list.html.twig @@ -0,0 +1,27 @@ +{# +/** + * @file + * Default theme implementation for a list of maintenance tasks to perform. + * + * Available variables: + * - tasks: A list of maintenance tasks to perform. Each item in the list has + * the following variables: + * - item: The maintenance task. + * - attributes: HTML attributes for the maintenance task. + * - status: (optional) Text describing the status of the maintenance task, + * 'active' or 'done'. + * + * @ingroup themeable + */ +#} +

    Installation tasks

    +
      +{% for task in tasks %} + + {{- task.item -}} + {%- if task.status -%} + ({{ task.status }}) + {%- endif -%} + +{% endfor %} +
    diff --git a/core/modules/system/tests/modules/database_test/database_test.install b/core/modules/system/tests/modules/database_test/database_test.install index 866d453..867d813 100644 --- a/core/modules/system/tests/modules/database_test/database_test.install +++ b/core/modules/system/tests/modules/database_test/database_test.install @@ -87,37 +87,6 @@ function database_test_schema() { ), ); - $schema['test_people_copy'] = array( - 'description' => 'A duplicate version of the test_people table, used for additional tests.', - 'fields' => array( - 'name' => array( - 'description' => "A person's name", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'age' => array( - 'description' => "The person's age", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'job' => array( - 'description' => "The person's job", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('job'), - 'indexes' => array( - 'ages' => array('age'), - ), - ); - $schema['test_one_blob'] = array( 'description' => 'A simple table including a BLOB field for testing BLOB behavior.', 'fields' => array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php index 938cc7b..74c3d12 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php @@ -7,14 +7,66 @@ namespace Drupal\taxonomy\Controller; -use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\taxonomy\TermStorageControllerInterface; use Drupal\taxonomy\VocabularyInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides route responses for taxonomy.module. */ -class TaxonomyController extends ControllerBase { +class TaxonomyController implements ControllerInterface { + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** + * The taxonomy term storage. + * + * @var \Drupal\taxonomy\TermStorageControllerInterface + */ + protected $termStorage; + + /** + * Constructs a new TaxonomyController. + * + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + * @param \Drupal\taxonomy\TermStorageControllerInterface $term_storage + * The taxonomy term storage. + */ + public function __construct(ModuleHandlerInterface $module_handler, EntityManager $entity_manager, TermStorageControllerInterface $term_storage) { + $this->moduleHandler = $module_handler; + $this->entityManager = $entity_manager; + $this->termStorage = $term_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + $entity_manager = $container->get('plugin.manager.entity'); + return new static( + $container->get('module_handler'), + $entity_manager, + $entity_manager->getStorageController('taxonomy_term') + ); + } /** * Returns a rendered edit form to create a new term associated to the given vocabulary. @@ -26,11 +78,11 @@ class TaxonomyController extends ControllerBase { * The taxonomy term add form. */ public function addForm(VocabularyInterface $taxonomy_vocabulary) { - $term = $this->entityManager()->getStorageController('taxonomy_term')->create(array('vid' => $taxonomy_vocabulary->id())); - if ($this->moduleHandler()->moduleExists('language')) { + $term = $this->termStorage->create(array('vid' => $taxonomy_vocabulary->id())); + if ($this->moduleHandler->moduleExists('language')) { $term->langcode = language_get_default_langcode('taxonomy_term', $taxonomy_vocabulary->id()); } - return $this->entityManager()->getForm($term); + return $this->entityManager->getForm($term); } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php index 87f247c..edd0b8d 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php @@ -74,7 +74,7 @@ public function buildRow(EntityInterface $entity) { unset($row['id']); $row['label'] = array( - '#markup' => $row['label'], + '#markup' => check_plain($row['label']), ); $row['#weight'] = $entity->get('weight'); // Add weight column. diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index c4f950b..876c49c 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -39,11 +39,11 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) { $parent_fields = FALSE; $page = isset($_GET['page']) ? $_GET['page'] : 0; - $page_increment = config('taxonomy.settings')->get('terms_per_page_admin'); - $page_entries = 0; - $before_entries = 0; - $after_entries = 0; - $root_entries = 0; + $page_increment = config('taxonomy.settings')->get('terms_per_page_admin'); // Number of terms per page. + $page_entries = 0; // Elements shown on this page. + $before_entries = 0; // Elements at the root level before this page. + $after_entries = 0; // Elements at the root level after this page. + $root_entries = 0; // Elements at the root level on this page. // Terms from previous and next pages are shown if the term tree would have // been cut in the middle. Keep track of how many extra terms we show on each @@ -83,8 +83,7 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) { $back_step++; if ($pterm->depth == 0) { prev($tree); - // Jump back to the start of the root level parent. - continue 2; + continue 2; // Jump back to the start of the root level parent. } } } @@ -126,10 +125,8 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) { // If this form was already submitted once, it's probably hit a validation // error. Ensure the form is rebuilt in the same order as the user submitted. if (!empty($form_state['input'])) { - // Get the $_POST order. - $order = array_flip(array_keys($form_state['input']['terms'])); - // Update our form with the new order. - $current_page = array_merge($order, $current_page); + $order = array_flip(array_keys($form_state['input']['terms'])); // Get the $_POST order. + $current_page = array_merge($order, $current_page); // Update our form with the new order. foreach ($current_page as $key => $term) { // Verify this is a term for the current page and set at the current // depth. diff --git a/core/modules/telephone/lib/Drupal/telephone/Plugin/field/field_type/TelephoneItem.php b/core/modules/telephone/lib/Drupal/telephone/Plugin/field/field_type/TelephoneItem.php index efe2bcf..2c63ea7 100644 --- a/core/modules/telephone/lib/Drupal/telephone/Plugin/field/field_type/TelephoneItem.php +++ b/core/modules/telephone/lib/Drupal/telephone/Plugin/field/field_type/TelephoneItem.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'telephone' field type. @@ -35,7 +35,7 @@ class TelephoneItem extends ConfigFieldItemBase { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php index d048caf..ca81ddd 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'text' field type. @@ -33,7 +33,7 @@ class TextItem extends TextItemBase { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextLongItem.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextLongItem.php index 5531890..8def87f 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextLongItem.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextLongItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'text_long' field type. @@ -30,7 +30,7 @@ class TextLongItem extends TextItemBase { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php index 8d5c9fb..0ea7b3e 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Annotation\FieldType; use Drupal\Core\Annotation\Translation; -use Drupal\field\FieldInterface; +use Drupal\field\Plugin\Core\Entity\Field; /** * Plugin implementation of the 'text_with_summary' field type. @@ -63,7 +63,7 @@ public function getPropertyDefinitions() { /** * {@inheritdoc} */ - public static function schema(FieldInterface $field) { + public static function schema(Field $field) { return array( 'columns' => array( 'value' => array( diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php index 8e9ed6e..a302a50 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php @@ -7,9 +7,9 @@ namespace Drupal\user\Plugin\views\field; -use Drupal\views\Plugin\views\field\ActionBulkForm; use Drupal\Component\Annotation\PluginID; use Drupal\Core\Entity\EntityManager; +use Drupal\system\Plugin\views\field\BulkFormBase; use Drupal\user\UserInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -18,7 +18,7 @@ * * @PluginID("user_bulk_form") */ -class UserBulkForm extends ActionBulkForm { +class UserBulkForm extends BulkFormBase { /** * {@inheritdoc} diff --git a/core/modules/user/lib/Drupal/user/RoleListController.php b/core/modules/user/lib/Drupal/user/RoleListController.php index 3f92dfd..a2250f5 100644 --- a/core/modules/user/lib/Drupal/user/RoleListController.php +++ b/core/modules/user/lib/Drupal/user/RoleListController.php @@ -63,7 +63,7 @@ public function buildRow(EntityInterface $entity) { unset($row['id']); $row['label'] = array( - '#markup' => $row['label'], + '#markup' => check_plain($row['label']), ); $row['#weight'] = $entity->get('weight'); // Add weight column. diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php b/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php index 05e5f53..9d714a0 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php @@ -42,15 +42,15 @@ function testSecondaryMenu() { // For a logged-in user, expect the secondary menu to have links for "My // account" and "Log out". - $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array( - ':menu_class' => 'links', + $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( + ':menu_id' => 'secondary-menu', ':href' => 'user', ':text' => 'My account', )); $this->assertEqual(count($link), 1, 'My account link is in secondary menu.'); - $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array( - ':menu_class' => 'links', + $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( + ':menu_id' => 'secondary-menu', ':href' => 'user/logout', ':text' => 'Log out', )); diff --git a/core/modules/user/tests/Drupal/user/Tests/Plugin/views/field/UserBulkFormTest.php b/core/modules/user/tests/Drupal/user/Tests/Plugin/views/field/UserBulkFormTest.php deleted file mode 100644 index 1214fc6..0000000 --- a/core/modules/user/tests/Drupal/user/Tests/Plugin/views/field/UserBulkFormTest.php +++ /dev/null @@ -1,70 +0,0 @@ - 'User: Bulk form', - 'description' => 'Tests the user bulk form plugin.', - 'group' => 'Views module integration', - ); - } - - /** - * Tests the constructor assignment of actions. - */ - public function testConstructor() { - $actions = array(); - - for ($i = 1; $i <= 2; $i++) { - $action = $this->getMockBuilder('Drupal\system\Plugin\Core\Entity\Action') - ->disableOriginalConstructor() - ->getMock(); - $action->expects($this->any()) - ->method('getType') - ->will($this->returnValue('user')); - $actions[$i] = $action; - } - - $action = $this->getMockBuilder('Drupal\system\Plugin\Core\Entity\Action') - ->disableOriginalConstructor() - ->getMock(); - $action->expects($this->any()) - ->method('getType') - ->will($this->returnValue('node')); - $actions[] = $action; - - $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager') - ->disableOriginalConstructor() - ->getMock(); - $storage_controller = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface'); - $storage_controller->expects($this->any()) - ->method('loadMultiple') - ->will($this->returnValue($actions)); - - $entity_manager->expects($this->any()) - ->method('getStorageController') - ->with('action') - ->will($this->returnValue($storage_controller)); - - $user_bulk_form = new UserBulkForm(array(), 'user_bulk_form', array(), $entity_manager); - - $this->assertAttributeEquals(array_slice($actions, 0, -1, TRUE), 'actions', $user_bulk_form); - } - -} diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php index d3cada4..c749a19 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php @@ -7,10 +7,13 @@ namespace Drupal\views\Plugin\Block; +use Drupal\block\BlockBase; use Drupal\Component\Annotation\Plugin; use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Component\Utility\Xss; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\views\ViewExecutableFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,7 +26,85 @@ * derivative = "Drupal\views\Plugin\Derivative\ViewsBlock" * ) */ -class ViewsBlock extends ViewsBlockBase { +class ViewsBlock extends BlockBase implements ContainerFactoryPluginInterface { + + /** + * The View executable object. + * + * @var \Drupal\views\ViewExecutable + */ + protected $view; + + /** + * The display ID being used for this View. + * + * @var string + */ + protected $displayID; + + /** + * Indicates whether the display was successfully set. + * + * @var bool + */ + protected $displaySet; + + /** + * Constructs a Drupal\Component\Plugin\PluginBase object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param array $plugin_definition + * The plugin implementation definition. + * @param \Drupal\views\ViewExecutableFactory $executable_factory + * The view executable factory. + * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller + * The views storage controller. + */ + public function __construct(array $configuration, $plugin_id, array $plugin_definition, ViewExecutableFactory $executable_factory, EntityStorageControllerInterface $storage_controller) { + $this->pluginId = $plugin_id; + list($plugin, $delta) = explode(':', $this->getPluginId()); + list($name, $this->displayID) = explode('-', $delta, 2); + // Load the view. + $view = $storage_controller->load($name); + $this->view = $executable_factory->get($view); + $this->displaySet = $this->view->setDisplay($this->displayID); + + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { + return new static( + $configuration, $plugin_id, $plugin_definition, + $container->get('views.executable'), + $container->get('plugin.manager.entity')->getStorageController('view') + ); + } + + /** + * Overrides \Drupal\block\BlockBase::access(). + */ + public function access() { + return $this->view->access($this->displayID); + } + + /** + * Overrides \Drupal\block\BlockBase::form(). + */ + public function buildConfigurationForm(array $form, array &$form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + // Set the default label to '' so the views internal title is used. + $form['label']['#default_value'] = ''; + $form['label']['#access'] = FALSE; + + return $form; + } /** * {@inheritdoc} @@ -63,8 +144,6 @@ public function blockForm($form, &$form_state) { if ($this->displaySet) { return $this->view->display_handler->blockForm($this, $form, $form_state); } - - return array(); } /** @@ -86,6 +165,31 @@ public function blockSubmit($form, &$form_state) { } /** + * Converts Views block content to a renderable array with contextual links. + * + * @param string|array $output + * An string|array representing the block. This will be modified to be a + * renderable array, containing the optional '#contextual_links' property (if + * there are any contextual links associated with the block). + * @param string $block_type + * The type of the block. If it's 'block' it's a regular views display, + * but 'exposed_filter' exist as well. + */ + protected function addContextualLinks(&$output, $block_type = 'block') { + // Do not add contextual links to an empty block. + if (!empty($output)) { + // Contextual links only work on blocks whose content is a renderable + // array, so if the block contains a string of already-rendered markup, + // convert it to an array. + if (is_string($output)) { + $output = array('#markup' => $output); + } + // Add the contextual links. + views_add_contextual_links($output, $block_type, $this->view, $this->displayID); + } + } + + /** * Generates a views block instance ID. * * @param \Drupal\Core\Entity\EntityStorageControllerInterface $manager diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlockBase.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlockBase.php deleted file mode 100644 index b90b3e6..0000000 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlockBase.php +++ /dev/null @@ -1,124 +0,0 @@ -pluginId = $plugin_id; - list($plugin, $delta) = explode(':', $this->getPluginId()); - list($name, $this->displayID) = explode('-', $delta, 2); - // Load the view. - $view = $storage_controller->load($name); - $this->view = $executable_factory->get($view); - $this->displaySet = $this->view->setDisplay($this->displayID); - - parent::__construct($configuration, $plugin_id, $plugin_definition); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { - return new static( - $configuration, $plugin_id, $plugin_definition, - $container->get('views.executable'), - $container->get('plugin.manager.entity')->getStorageController('view') - ); - } - - /** - * {@inheritdoc} - */ - public function access() { - return $this->view->access($this->displayID); - } - - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, array &$form_state) { - $form = parent::buildConfigurationForm($form, $form_state); - - // Set the default label to '' so the views internal title is used. - $form['label']['#default_value'] = ''; - $form['label']['#access'] = FALSE; - - return $form; - } - - /** - * Converts Views block content to a renderable array with contextual links. - * - * @param string|array $output - * An string|array representing the block. This will be modified to be a - * renderable array, containing the optional '#contextual_links' property (if - * there are any contextual links associated with the block). - * @param string $block_type - * The type of the block. If it's 'block' it's a regular views display, - * but 'exposed_filter' exist as well. - */ - protected function addContextualLinks(&$output, $block_type = 'block') { - // Do not add contextual links to an empty block. - if (!empty($output)) { - // Contextual links only work on blocks whose content is a renderable - // array, so if the block contains a string of already-rendered markup, - // convert it to an array. - if (is_string($output)) { - $output = array('#markup' => $output); - } - // Add the contextual links. - views_add_contextual_links($output, $block_type, $this->view, $this->displayID); - } - } - -} diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsExposedFilterBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsExposedFilterBlock.php index 3ef4ecd..577caa5 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsExposedFilterBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsExposedFilterBlock.php @@ -20,7 +20,7 @@ * derivative = "Drupal\views\Plugin\Derivative\ViewsExposedFilterBlock" * ) */ -class ViewsExposedFilterBlock extends ViewsBlockBase { +class ViewsExposedFilterBlock extends ViewsBlock { /** * {@inheritdoc} @@ -31,6 +31,7 @@ public function build() { // contextual links. $this->addContextualLinks($output, 'exposed_filter'); + $this->view->destroy(); return $output; } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/ActionBulkForm.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/ActionBulkForm.php deleted file mode 100644 index ba2356c..0000000 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/ActionBulkForm.php +++ /dev/null @@ -1,116 +0,0 @@ - 'exclude', - ); - $options['selected_actions'] = array( - 'default' => array(), - ); - return $options; - } - - /** - * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::buildOptionsForm(). - */ - public function buildOptionsForm(&$form, &$form_state) { - parent::buildOptionsForm($form, $form_state); - - $form['include_exclude'] = array( - '#type' => 'radios', - '#title' => t('Available actions'), - '#options' => array( - 'exclude' => t('All actions, except selected'), - 'include' => t('Only selected actions'), - ), - '#default_value' => $this->options['include_exclude'], - ); - $form['selected_actions'] = array( - '#type' => 'checkboxes', - '#title' => t('Selected actions'), - '#options' => $this->getBulkOptions(FALSE), - '#default_value' => $this->options['selected_actions'], - ); - } - - /** - * Overrides \Drupal\views\Plugin\views\PluginBase::buildOptionsForm(). - */ - public function validateOptionsForm(&$form, &$form_state) { - parent::validateOptionsForm($form, $form_state); - - $form_state['values']['options']['selected_actions'] = array_filter($form_state['values']['options']['selected_actions']); - } - - /** - * Implements \Drupal\system\Plugin\views\field\BulkFormBase::getBulkOptions(). - * - * @param bool $filtered - * (optional) Whether to filter actions to selected actions. - */ - protected function getBulkOptions($filtered = TRUE) { - // Get all available actions. - $entity_type = $this->getEntityType(); - $options = array(); - // Filter the action list. - foreach ($this->actions as $id => $action) { - if ($filtered) { - $in_selected = in_array($id, $this->options['selected_actions']); - // If the field is configured to include only the selected actions, - // skip actions that were not selected. - if (($this->options['include_exclude'] == 'include') && !$in_selected) { - continue; - } - // Otherwise, if the field is configured to exclude the selected - // actions, skip actions that were selected. - elseif (($this->options['include_exclude'] == 'exclude') && $in_selected) { - continue; - } - } - // Only allow actions that are valid for this entity type. - if (($action->getType() == $entity_type)) { - $options[$id] = $action->label(); - } - } - - return $options; - } - - /** - * Implements \Drupal\system\Plugin\views\field\BulkFormBase::views_form_submit(). - */ - public function views_form_submit(&$form, &$form_state) { - parent::views_form_submit($form, $form_state); - if ($form_state['step'] == 'views_form_views_form') { - $count = count(array_filter($form_state['values'][$this->options['id']])); - $action = $this->actions[$form_state['values']['action']]; - if ($count) { - drupal_set_message(format_plural($count, '%action was applied to @count item.', '%action was applied to @count items.', array( - '%action' => $action->label(), - ))); - } - } - } - -} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperatorString.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperatorString.php index 5f3537d..b92bcbf 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperatorString.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperatorString.php @@ -37,7 +37,7 @@ public function query() { else { $where .= "<> ''"; } - $this->query->addWhereExpression($this->options['group'], $where); + $this->query->addWhere($this->options['group'], $where); } } diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterBooleanOperatorStringTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterBooleanOperatorStringTest.php deleted file mode 100644 index d3c3862..0000000 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterBooleanOperatorStringTest.php +++ /dev/null @@ -1,232 +0,0 @@ - 'id', - ); - - public static function getInfo() { - return array( - 'name' => 'Filter: Boolean string operator', - 'description' => 'Test the core Drupal\views\Plugin\views\filter\BooleanOperatorString handler.', - 'group' => 'Views Handlers', - ); - } - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('menu_router', 'variable', 'key_value_expire')); - } - - - /** - * {@inheritdoc} - */ - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - - $schema['views_test_data']['fields']['status'] = array( - 'description' => 'The status of this record', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ); - - return $schema; - } - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $views_data = parent::viewsData(); - - $views_data['views_test_data']['status']['filter']['id'] = 'boolean_string'; - - return $views_data; - } - - /** - * {@inheritdoc} - */ - protected function dataSet() { - $data = parent::dataSet(); - - foreach ($data as &$row) { - if ($row['status']) { - $row['status'] = 'Enabled'; - } - else { - $row['status'] = ''; - } - } - - return $data; - } - - /** - * Tests the BooleanOperatorString filter. - */ - public function testFilterBooleanOperatorString() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 0, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->column_map); - - $view->destroy(); - $view->setDisplay(); - - // Add the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 1, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->column_map); - } - - /** - * Tests the Boolean filter with grouped exposed form enabled. - */ - public function testFilterGroupedExposed() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - - $view->setExposedInput(array('status' => 1)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->column_map); - $view->destroy(); - - $view->setExposedInput(array('status' => 2)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->column_map); - } - - /** - * Provides grouped exposed filter configuration. - * - * @return array - * Returns the filter configuration for exposed filters. - */ - protected function getGroupedExposedFilters() { - $filters = array( - 'status' => array( - 'id' => 'status', - 'table' => 'views_test_data', - 'field' => 'status', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'status_op', - 'label' => 'status', - 'identifier' => 'status', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'status', - 'identifier' => 'status', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Active', - 'operator' => '=', - 'value' => '1', - ), - 2 => array( - 'title' => 'Blocked', - 'operator' => '=', - 'value' => '0', - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php index 05b7035..d491d1b 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php @@ -8,8 +8,6 @@ namespace Drupal\views\Tests\Plugin; use Drupal\views\Tests\ViewTestBase; -use Drupal\views\ViewExecutable; -use Drupal\views\Views; /** * Tests exposed forms. @@ -21,14 +19,14 @@ class ExposedFormTest extends ViewTestBase { * * @var array */ - public static $testViews = array('test_reset_button', 'test_exposed_block'); + public static $testViews = array('test_reset_button'); /** * Modules to enable. * * @var array */ - public static $modules = array('views_ui', 'block'); + public static $modules = array('views_ui'); public static function getInfo() { return array( @@ -104,45 +102,11 @@ public function testExposedFormRender() { $output = $exposed_form->renderExposedForm(); $this->drupalSetContent(drupal_render($output)); - $this->assertFieldByXpath('//form/@id', $this->getExpectedExposedFormId($view), 'Expected form ID found.'); + $expected_id = drupal_clean_css_identifier('views-exposed-form-' . $view->storage->id() . '-' . $view->current_display); + $this->assertFieldByXpath('//form/@id', $expected_id, 'Expected form ID found.'); $expected_action = url($view->display_handler->getUrl()); $this->assertFieldByXPath('//form/@action', $expected_action, 'The expected value for the action attribute was found.'); } - /** - * Tests the exposed block functionality. - */ - public function testExposedBlock() { - $view = Views::getView('test_exposed_block'); - $view->setDisplay('page_1'); - $block = $this->drupalPlaceBlock('views_exposed_filter_block:test_exposed_block-page_1'); - $this->drupalGet('test_exposed_block'); - - // Test there is an exposed form in a block. - $xpath = $this->buildXPathQuery('//div[@id=:id]/div/form/@id', array(':id' => 'block-' . $block->get('machine_name'))); - $this->assertFieldByXpath($xpath, $this->getExpectedExposedFormId($view), 'Expected form found in views block.'); - - // Test there is not an exposed form in the view page content area. - $xpath = $this->buildXPathQuery('//div[@class="view-content"]/form/@id', array(':id' => 'block-' . $block->get('machine_name'))); - $this->assertNoFieldByXpath($xpath, $this->getExpectedExposedFormId($view), 'No exposed form found in views content region.'); - - // Test there is only one views exposed form on the page. - $elements = $this->xpath('//form[@id=:id]', array(':id' => $this->getExpectedExposedFormId($view))); - $this->assertEqual(count($elements), 1, 'One exposed form block found.'); - } - - /** - * Returns a views exposed form ID. - * - * @param \Drupal\views\ViewExecutable $view - * The view to create an ID for. - * - * @return string - * The form ID. - */ - protected function getExpectedExposedFormId(ViewExecutable $view) { - return drupal_clean_css_identifier('views-exposed-form-' . $view->storage->id() . '-' . $view->current_display); - } - } diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableUnitTest.php index f5548f7..6af4636 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableUnitTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableUnitTest.php @@ -127,15 +127,6 @@ public function testTable() { $view->destroy(); - // Excluded field to make sure its wrapping td doesn't show. - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $view->field['name']->options['exclude'] = TRUE; - $output = $view->preview(); - $output = drupal_render($output); - $this->assertFalse(strpos($output, 'views-field-name') !== FALSE, "Excluded field's wrapper was not rendered."); - $view->destroy(); - // Render a non empty result, and ensure that the empty area handler is not // rendered. $this->executeView($view); diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php index 2f1b2ab..5a00bd7 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php @@ -7,7 +7,6 @@ namespace Drupal\views\Tests; -use Drupal\Core\Database\Query\SelectInterface; use Drupal\simpletest\WebTestBase; use Drupal\views\ViewExecutable; use Drupal\block\Plugin\Core\Entity\Block; @@ -229,11 +228,7 @@ protected function executeView($view, $args = array()) { $view->setDisplay(); $view->preExecute($args); $view->execute(); - $verbose_message = '
    Executed view: ' . ((string) $view->build_info['query']). '
    '; - if ($view->build_info['query'] instanceof SelectInterface) { - $verbose_message .= '
    Arguments: ' . print_r($view->build_info['query']->getArguments(), TRUE) . '
    '; - } - $this->verbose($verbose_message); + $this->verbose('
    Executed view: ' . ((string) $view->build_info['query']) . '
    '); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php index ab8173a..1213cc0 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php @@ -7,7 +7,6 @@ namespace Drupal\views\Tests; -use Drupal\Core\Database\Query\SelectInterface; use Drupal\views\ViewExecutable; use Drupal\views\ViewsBundle; use Drupal\simpletest\DrupalUnitTestBase; @@ -223,11 +222,7 @@ protected function executeView($view, $args = array()) { $view->setDisplay(); $view->preExecute($args); $view->execute(); - $verbose_message = '
    Executed view: ' . ((string) $view->build_info['query']). '
    '; - if ($view->build_info['query'] instanceof SelectInterface) { - $verbose_message .= '
    Arguments: ' . print_r($view->build_info['query']->getArguments(), TRUE) . '
    '; - } - $this->verbose($verbose_message); + $this->verbose('
    Executed view: ' . ((string) $view->build_info['query']) . '
    '); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php index 8f3f4eb..0a8f18e 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php @@ -122,7 +122,7 @@ function testViewsWizardAndListing() { $this->assertLinkByHref(url($view3['page[path]'])); // Confirm that the block is available in the block administration UI. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertText('View: ' . $view3['label']); // Place the block. diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php index 485f048..e6808c7 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php @@ -75,7 +75,7 @@ function testItemsPerPage() { $this->assertTrue($pos5 < $pos4 && $pos4 < $pos3 && $pos3 < $pos2, 'The nodes appear in the expected order in the page display.'); // Confirm that the block is listed in the block administration UI. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertText('View: ' . $view['label']); // Place the block, visit a page that displays the block, and check that the diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_block.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_block.yml deleted file mode 100644 index 0528a1d..0000000 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_block.yml +++ /dev/null @@ -1,56 +0,0 @@ -base_table: node -core: '8' -description: '' -status: '1' -display: - default: - display_options: - access: - type: none - cache: - type: none - exposed_form: - options: - reset_button: '1' - type: basic - filters: - type: - expose: - identifier: type - label: 'Content: Type' - operator_id: type_op - reduce: '0' - exposed: '1' - field: type - id: type - table: node_field_data - plugin_id: node_type - provider: views - pager: - type: full - query: - options: - query_comment: '0' - type: views_query - style: - type: default - row: - type: 'entity:node' - options: - comments: '0' - links: '1' - display_plugin: default - display_title: Master - id: default - position: '0' - page_1: - display_options: - path: test_exposed_block - exposed_block: '1' - display_plugin: page - display_title: Page - id: page_1 - position: '0' -label: '' -id: test_exposed_block -tag: '' diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 4b90f77..bb312ff 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -485,6 +485,8 @@ function views_preprocess_html(&$variables) { if ($key !== FALSE) { unset($variables['attributes']['class'][$key]); $variables['attributes']['data-views-page-contextual-id'] = $variables['title_suffix']['contextual_links']['#id']; + // Add the JavaScript, with a group and weight such that it will run + // before modules/contextual/contextual.js. drupal_add_library('views', 'views.contextual-links'); } } @@ -819,7 +821,6 @@ function views_library_info() { 'title' => 'Views Contextual links', 'version' => VERSION, 'js' => array( - // Set to -10 to move it before the contextual links javascript file. "$path/js/views-contextual.js" => array('group' => JS_LIBRARY, 'weight' => -10), ), 'dependencies' => array( diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 0f44cd9..522552e 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -616,38 +616,27 @@ function template_preprocess_views_view_table(&$variables) { // Render each field into its appropriate column. foreach ($result as $num => $row) { - - // Skip building the attributes and content if the field is to be excluded - // from the display. - if (!empty($fields[$field]->options['exclude'])) { - continue; - } - - // Reference to the column in the loop to make the code easier to read. - $column_reference =& $variables['rows'][$num]['columns'][$column]; - // Add field classes. - $column_reference['attributes'] = array(); + $variables['rows'][$num]['columns'][$column]['attributes'] = array(); if ($fields[$field]->options['element_default_classes']) { - $column_reference['attributes']['class'][] = 'views-field'; - $column_reference['attributes']['class'][] = 'views-field-' . $variables['fields'][$field]; + $variables['rows'][$num]['columns'][$column]['attributes']['class'][] = 'views-field'; + $variables['rows'][$num]['columns'][$column]['attributes']['class'][] = 'views-field-' . $variables['fields'][$field]; } if ($classes = $fields[$field]->elementClasses($num)) { - $column_reference['attributes']['class'][] = $classes; + $variables['rows'][$num]['columns'][$column]['attributes']['class'][] = $classes; } - // Add responsive header classes. if (!empty($options['info'][$field]['responsive'])) { - $column_reference['attributes']['class'][] = $options['info'][$field]['responsive']; + $variables['rows'][$num]['columns'][$column]['attributes']['class'][] = $options['info'][$field]['responsive']; } // Improves accessibility of complex tables. if (isset($variables['header'][$field]['attributes']['id'])) { - $column_reference['attributes']['headers'] = array($variables['header'][$field]['attributes']['id']); + $variables['rows'][$num]['columns'][$column]['attributes']['headers'] = array($variables['header'][$field]['attributes']['id']); } - if (!empty($fields[$field])) { + if (!empty($fields[$field]) && empty($fields[$field]->options['exclude'])) { $field_output = $handler->getField($num, $field); $element_type = $fields[$field]->elementType(TRUE, TRUE); if ($element_type) { @@ -655,20 +644,20 @@ function template_preprocess_views_view_table(&$variables) { } // Only bother with separators and stuff if the field shows up. - if (!empty($field_output) && empty($column_reference['content'])) { + if (!empty($field_output) && empty($variables['rows'][$num]['columns'][$column]['content'])) { // Place the field into the column, along with an optional separator. - if (!empty($column_reference['content'])) { + if (!empty($variables['rows'][$num][$column]['content'])) { if (!empty($options['info'][$column]['separator'])) { - $column_reference['content'] .= filter_xss_admin($options['info'][$column]['separator']); + $variables['rows'][$num]['columns'][$column]['content'] .= filter_xss_admin($options['info'][$column]['separator']); } } else { - $column_reference['content'] = ''; + $variables['rows'][$num]['columns'][$column]['content'] = ''; } - $column_reference['content'] .= $field_output; + $variables['rows'][$num]['columns'][$column]['content'] .= $field_output; } } - $column_reference['attributes'] = new Attribute($column_reference['attributes']); + $variables['rows'][$num]['columns'][$column]['attributes'] = new Attribute($variables['rows'][$num]['columns'][$column]['attributes']); } // Remove columns if the option is hide empty column is checked and the diff --git a/core/modules/views_ui/js/views-admin.js b/core/modules/views_ui/js/views-admin.js index d20b0af..9dc1075 100644 --- a/core/modules/views_ui/js/views-admin.js +++ b/core/modules/views_ui/js/views-admin.js @@ -2,76 +2,78 @@ * @file * Some basic behaviors and utility functions for Views UI. */ -(function ($, Drupal, drupalSettings, debounce) { - -"use strict"; - Drupal.viewsUi = {}; +Drupal.behaviors.viewsUiEditView = {}; + /** * Improve the user experience of the views edit interface. */ -Drupal.behaviors.viewsUiEditView = { - attach: function () { - // Only show the SQL rewrite warning when the user has chosen the - // corresponding checkbox. - $('#edit-query-options-disable-sql-rewrite').on('click', function () { - $('.sql-rewrite-warning').toggleClass('js-hide'); - }); - } +Drupal.behaviors.viewsUiEditView.attach = function (context, settings) { + + "use strict"; + + // Only show the SQL rewrite warning when the user has chosen the + // corresponding checkbox. + jQuery('#edit-query-options-disable-sql-rewrite').click(function () { + jQuery('.sql-rewrite-warning').toggleClass('js-hide'); + }); }; +Drupal.behaviors.viewsUiAddView = {}; + /** * In the add view wizard, use the view name to prepopulate form fields such as * page title and menu link. */ -Drupal.behaviors.viewsUiAddView = { - attach: function (context) { - var $context = $(context); - // Set up regular expressions to allow only numbers, letters, and dashes. - var exclude = new RegExp('[^a-z0-9\\-]+', 'g'); - var replace = '-'; - var suffix; - - // The page title, block title, and menu link fields can all be prepopulated - // with the view name - no regular expression needed. - var $fields = $context.find('[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]'); - if ($fields.length) { - if (!this.fieldsFiller) { - this.fieldsFiller = new Drupal.viewsUi.FormFieldFiller($fields); - } - else { - // After an AJAX response, this.fieldsFiller will still have event - // handlers bound to the old version of the form fields (which don't exist - // anymore). The event handlers need to be unbound and then rebound to the - // new markup. Note that jQuery.live is difficult to make work in this - // case because the IDs of the form fields change on every AJAX response. - this.fieldsFiller.rebind($fields); - } +Drupal.behaviors.viewsUiAddView.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + var exclude, replace, suffix; + // Set up regular expressions to allow only numbers, letters, and dashes. + exclude = new RegExp('[^a-z0-9\\-]+', 'g'); + replace = '-'; + + // The page title, block title, and menu link fields can all be prepopulated + // with the view name - no regular expression needed. + var $fields = $(context).find('[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]'); + if ($fields.length) { + if (!this.fieldsFiller) { + this.fieldsFiller = new Drupal.viewsUi.FormFieldFiller($fields); } + else { + // After an AJAX response, this.fieldsFiller will still have event + // handlers bound to the old version of the form fields (which don't exist + // anymore). The event handlers need to be unbound and then rebound to the + // new markup. Note that jQuery.live is difficult to make work in this + // case because the IDs of the form fields change on every AJAX response. + this.fieldsFiller.rebind($fields); + } + } - // Prepopulate the path field with a URLified version of the view name. - var $pathField = $context.find('[id^="edit-page-path"]'); - if ($pathField.length) { - if (!this.pathFiller) { - this.pathFiller = new Drupal.viewsUi.FormFieldFiller($pathField, exclude, replace); - } - else { - this.pathFiller.rebind($pathField); - } + // Prepopulate the path field with a URLified version of the view name. + var $pathField = $(context).find('[id^="edit-page-path"]'); + if ($pathField.length) { + if (!this.pathFiller) { + this.pathFiller = new Drupal.viewsUi.FormFieldFiller($pathField, exclude, replace); } + else { + this.pathFiller.rebind($pathField); + } + } - // Populate the RSS feed field with a URLified version of the view name, and - // an .xml suffix (to make it unique). - var $feedField = $context.find('[id^="edit-page-feed-properties-path"]'); - if ($feedField.length) { - if (!this.feedFiller) { - suffix = '.xml'; - this.feedFiller = new Drupal.viewsUi.FormFieldFiller($feedField, exclude, replace, suffix); - } - else { - this.feedFiller.rebind($feedField); - } + // Populate the RSS feed field with a URLified version of the view name, and + // an .xml suffix (to make it unique). + var $feedField = $(context).find('[id^="edit-page-feed-properties-path"]'); + if ($feedField.length) { + if (!this.feedFiller) { + suffix = '.xml'; + this.feedFiller = new Drupal.viewsUi.FormFieldFiller($feedField, exclude, replace, suffix); + } + else { + this.feedFiller.rebind($feedField); } } }; @@ -93,6 +95,10 @@ Drupal.behaviors.viewsUiAddView = { * Optional. A suffix to append at the end of the target field content. */ Drupal.viewsUi.FormFieldFiller = function ($target, exclude, replace, suffix) { + + "use strict"; + + var $ = jQuery; this.source = $('#edit-label'); this.target = $target; this.exclude = exclude || false; @@ -112,85 +118,108 @@ Drupal.viewsUi.FormFieldFiller = function ($target, exclude, replace, suffix) { // Object constructor; no return value. }; -$.extend(Drupal.viewsUi.FormFieldFiller.prototype, { - /** - * Bind the form-filling behavior. - */ - bind: function () { - this.unbind(); - // Populate the form field when the source changes. - this.source.on('keyup.viewsUi change.viewsUi', this.populate); - // Quit populating the field as soon as it gets focus. - this.target.on('focus.viewsUi', this.unbind); - }, +/** + * Bind the form-filling behavior. + */ +Drupal.viewsUi.FormFieldFiller.prototype.bind = function () { - /** - * Get the source form field value as altered by the passed-in parameters. - */ - getTransliterated: function () { - var from = this.source.val(); - if (this.exclude) { - from = from.toLowerCase().replace(this.exclude, this.replace); - } - return from + this.suffix; - }, + "use strict"; - /** - * Populate the target form field with the altered source field value. - */ - _populate: function () { - var transliterated = this.getTransliterated(); - this.target.val(transliterated); - }, + this.unbind(); + // Populate the form field when the source changes. + this.source.bind('keyup.viewsUi change.viewsUi', this.populate); + // Quit populating the field as soon as it gets focus. + this.target.bind('focus.viewsUi', this.unbind); +}; - /** - * Stop prepopulating the form fields. - */ - _unbind: function () { - this.source.off('keyup.viewsUi change.viewsUi', this.populate); - this.target.off('focus.viewsUi', this.unbind); - }, +/** + * Get the source form field value as altered by the passed-in parameters. + */ +Drupal.viewsUi.FormFieldFiller.prototype.getTransliterated = function () { - /** - * Bind event handlers to the new form fields, after they're replaced via AJAX. - */ - rebind: function ($fields) { - this.target = $fields; - this.bind(); - }}); - - -Drupal.behaviors.addItemForm = { - attach: function (context) { - // The add item form may have an id of views-ui-add-item-form--n. - var $form = $(context).find('form[id^="views-ui-add-item-form"]').first(); - // Make sure we don't add more than one event handler to the same form. - $form = $form.once('views-ui-add-item-form'); - if ($form.length) { - new Drupal.viewsUi.AddItemForm($form); - } + "use strict"; + + var from = this.source.val(); + if (this.exclude) { + from = from.toLowerCase().replace(this.exclude, this.replace); } + return from + this.suffix; }; -Drupal.viewsUi.AddItemForm = function ($form) { +/** + * Populate the target form field with the altered source field value. + */ +Drupal.viewsUi.FormFieldFiller.prototype._populate = function () { + + "use strict"; + + var transliterated = this.getTransliterated(); + this.target.val(transliterated); +}; + +/** + * Stop prepopulating the form fields. + */ +Drupal.viewsUi.FormFieldFiller.prototype._unbind = function () { + + "use strict"; + + this.source.unbind('keyup.viewsUi change.viewsUi', this.populate); + this.target.unbind('focus.viewsUi', this.unbind); +}; + +/** + * Bind event handlers to the new form fields, after they're replaced via AJAX. + */ +Drupal.viewsUi.FormFieldFiller.prototype.rebind = function ($fields) { + + "use strict"; + + this.target = $fields; + this.bind(); +}; + +Drupal.behaviors.addItemForm = {}; +Drupal.behaviors.addItemForm.attach = function (context) { + + "use strict"; + + var $ = jQuery; + // The add item form may have an id of views-ui-add-item-form--n. + var $form = $(context).find('form[id^="views-ui-add-item-form"]').first(); + // Make sure we don't add more than one event handler to the same form. + $form = $form.once('views-ui-add-item-form'); + if ($form.length) { + new Drupal.viewsUi.addItemForm($form); + } +}; + +Drupal.viewsUi.addItemForm = function($form) { + + "use strict"; + this.$form = $form; - this.$form.find('.views-filterable-options :checkbox').on('click', $.proxy(this.handleCheck, this)); + this.$form.find('.views-filterable-options :checkbox').click(jQuery.proxy(this.handleCheck, this)); // Find the wrapper of the displayed text. this.$selected_div = this.$form.find('.views-selected-options').parent(); this.$selected_div.hide(); this.checkedItems = []; }; -Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) { - var $target = $(event.target); - var label = $.trim($target.next().text()); +Drupal.viewsUi.addItemForm.prototype.handleCheck = function (event) { + + "use strict"; + + var $target = jQuery(event.target); + var label = jQuery.trim($target.next().text()); // Add/remove the checked item to the list. if ($target.is(':checked')) { this.$selected_div.show(); this.checkedItems.push(label); } else { - var position = $.inArray(label, this.checkedItems); + var length = this.checkedItems.length; + var position = jQuery.inArray(label, this.checkedItems); // Delete the item from the list and take sure that the list doesn't have undefined items left. for (var i = 0; i < this.checkedItems.length; i++) { if (i === position) { @@ -210,7 +239,10 @@ Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) { /** * Refresh the display of the checked items. */ -Drupal.viewsUi.AddItemForm.prototype.refreshCheckedItems = function () { +Drupal.viewsUi.addItemForm.prototype.refreshCheckedItems = function() { + + "use strict"; + // Perhaps we should precache the text div, too. this.$selected_div.find('.views-selected-options').html(this.checkedItems.join(', ')); Drupal.viewsUi.resizeModal('', true); @@ -221,51 +253,55 @@ Drupal.viewsUi.AddItemForm.prototype.refreshCheckedItems = function () { * The following behavior detaches the elements from the DOM, wraps them * in an unordered list, then appends them to the list of tabs. */ -Drupal.behaviors.viewsUiRenderAddViewButton = { - attach: function (context) { - // Build the add display menu and pull the display input buttons into it. - var $menu = $(context).find('#views-display-menu-tabs').once('views-ui-render-add-view-button-processed'); - if (!$menu.length) { - return; - } +Drupal.behaviors.viewsUiRenderAddViewButton = {}; - var $addDisplayDropdown = $('
  • ' + Drupal.t('Add') + '
  • '); - var $displayButtons = $menu.nextAll('input.add-display').detach(); - $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('
  • ') - .parent().first().addClass('first').end().last().addClass('last'); - // Remove the 'Add ' prefix from the button labels since they're being palced - // in an 'Add' dropdown. - // @todo This assumes English, but so does $addDisplayDropdown above. Add - // support for translation. - $displayButtons.each(function () { - var label = $(this).val(); - if (label.substr(0, 4) === 'Add ') { - $(this).val(label.substr(4)); - } - }); - $addDisplayDropdown.appendTo($menu); +Drupal.behaviors.viewsUiRenderAddViewButton.attach = function (context, settings) { - // Add the click handler for the add display button - $menu.find('li.add > a').on('click', function (event) { - event.preventDefault(); - var $trigger = $(this); - Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger); - }); - // Add a mouseleave handler to close the dropdown when the user mouses - // away from the item. We use mouseleave instead of mouseout because - // the user is going to trigger mouseout when she moves from the trigger - // link to the sub menu items. - // We use the live binder because the open class on this item will be - // toggled on and off and we want the handler to take effect in the cases - // that the class is present, but not when it isn't. - $('li.add', $menu).on('mouseleave', function (event) { - var $this = $(this); - var $trigger = $this.children('a[href="#"]'); - if ($this.children('.action-list').is(':visible')) { - Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger); - } - }); + "use strict"; + + var $ = jQuery; + // Build the add display menu and pull the display input buttons into it. + var $menu = $('#views-display-menu-tabs', context).once('views-ui-render-add-view-button-processed'); + + if (!$menu.length) { + return; } + var $addDisplayDropdown = $('
  • ' + Drupal.t('Add') + '
  • '); + var $displayButtons = $menu.nextAll('input.add-display').detach(); + $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('
  • ') + .parent().first().addClass('first').end().last().addClass('last'); + // Remove the 'Add ' prefix from the button labels since they're being palced + // in an 'Add' dropdown. + // @todo This assumes English, but so does $addDisplayDropdown above. Add + // support for translation. + $displayButtons.each(function () { + var label = $(this).val(); + if (label.substr(0, 4) === 'Add ') { + $(this).val(label.substr(4)); + } + }); + $addDisplayDropdown.appendTo($menu); + + // Add the click handler for the add display button + $('li.add > a', $menu).bind('click', function (event) { + event.preventDefault(); + var $trigger = $(this); + Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger); + }); + // Add a mouseleave handler to close the dropdown when the user mouses + // away from the item. We use mouseleave instead of mouseout because + // the user is going to trigger mouseout when she moves from the trigger + // link to the sub menu items. + // We use the live binder because the open class on this item will be + // toggled on and off and we want the handler to take effect in the cases + // that the class is present, but not when it isn't. + $('li.add', $menu).on('mouseleave', function (event) { + var $this = $(this); + var $trigger = $this.children('a[href="#"]'); + if ($this.children('.action-list').is(':visible')) { + Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger); + } + }); }; /** @@ -273,19 +309,26 @@ Drupal.behaviors.viewsUiRenderAddViewButton = { * not written specifically for this UI, but I'm not sure where to put it. */ Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function ($trigger) { + + "use strict"; + $trigger.parent().toggleClass('open'); $trigger.next().slideToggle('fast'); }; -Drupal.behaviors.viewsUiSearchOptions = { - attach: function (context) { - // The add item form may have an id of views-ui-add-item-form--n. - var $form = $(context).find('form[id^="views-ui-add-item-form"]').first(); - // Make sure we don't add more than one event handler to the same form. - $form = $form.once('views-ui-filter-options'); - if ($form.length) { - new Drupal.viewsUi.OptionsSearch($form); - } +Drupal.behaviors.viewsUiSearchOptions = {}; + +Drupal.behaviors.viewsUiSearchOptions.attach = function (context) { + + "use strict"; + + var $ = jQuery; + // The add item form may have an id of views-ui-add-item-form--n. + var $form = $(context).find('form[id^="views-ui-add-item-form"]').first(); + // Make sure we don't add more than one event handler to the same form. + $form = $form.once('views-ui-filter-options'); + if ($form.length) { + new Drupal.viewsUi.OptionsSearch($form); } }; @@ -297,152 +340,171 @@ Drupal.behaviors.viewsUiSearchOptions = { * containing "taxonomy" in their label. */ Drupal.viewsUi.OptionsSearch = function ($form) { + + "use strict"; + this.$form = $form; // Add a keyup handler to the search box. this.$searchBox = this.$form.find('#edit-override-controls-options-search'); - this.$searchBox.keyup($.proxy(this.handleKeyup, this)); + this.$searchBox.keyup(jQuery.proxy(this.handleKeyup, this)); // Get a list of option labels and their corresponding divs and maintain it // in memory, so we have as little overhead as possible at keyup time. this.options = this.getOptions(this.$form.find('.filterable-option')); // Restripe on initial loading. this.handleKeyup(); // Trap the ENTER key in the search box so that it doesn't submit the form. - this.$searchBox.on('keypress', function (event) { + this.$searchBox.keypress(function(event) { if (event.which === 13) { event.preventDefault(); } }); }; -$.extend(Drupal.viewsUi.OptionsSearch.prototype, { - /** - * Assemble a list of all the filterable options on the form. - * - * @param $allOptions - * A $ object representing the rows of filterable options to be - * shown and hidden depending on the user's search terms. - */ - getOptions: function ($allOptions) { - var i, $label, $description, $option; - var options = []; - var length = $allOptions.length; - for (i = 0; i < length; i++) { - $option = $($allOptions[i]); - $label = $option.find('label'); - $description = $option.find('div.description'); - options[i] = { - // Search on the lowercase version of the label text + description. - 'searchText': $label.text().toLowerCase() + " " + $description.text().toLowerCase(), - // Maintain a reference to the jQuery object for each row, so we don't - // have to create a new object inside the performance-sensitive keyup - // handler. - '$div': $option - }; - } - return options; - }, - - /** - * Keyup handler for the search box that hides or shows the relevant options. - */ - handleKeyup: function (event) { - var found, i, j, option, search, words, wordsLength, zebraClass, zebraCounter; - - // Determine the user's search query. The search text has been converted to - // lowercase. - search = this.$searchBox.val().toLowerCase(); - words = search.split(' '); - wordsLength = words.length; - - // Start the counter for restriping rows. - zebraCounter = 0; - - // Search through the search texts in the form for matching text. - var length = this.options.length; - for (i = 0; i < length; i++) { - // Use a local variable for the option being searched, for performance. - option = this.options[i]; - found = true; - // Each word in the search string has to match the item in order for the - // item to be shown. - for (j = 0; j < wordsLength; j++) { - if (option.searchText.indexOf(words[j]) === -1) { - found = false; - } - } - if (found) { - // Show the checkbox row, and restripe it. - zebraClass = (zebraCounter % 2) ? 'odd' : 'even'; - option.$div.show(); - option.$div.removeClass('even odd'); - option.$div.addClass(zebraClass); - zebraCounter++; - } - else { - // The search string wasn't found; hide this item. - option.$div.hide(); - } - } +/** + * Assemble a list of all the filterable options on the form. + * + * @param $allOptions + * A jQuery object representing the rows of filterable options to be + * shown and hidden depending on the user's search terms. + */ +Drupal.viewsUi.OptionsSearch.prototype.getOptions = function ($allOptions) { + + "use strict"; + + var $ = jQuery; + var i, $label, $description, $option; + var options = []; + var length = $allOptions.length; + for (i = 0; i < length; i++) { + $option = $($allOptions[i]); + $label = $option.find('label'); + $description = $option.find('div.description'); + options[i] = { + // Search on the lowercase version of the label text + description. + 'searchText': $label.text().toLowerCase() + " " + $description.text().toLowerCase(), + // Maintain a reference to the jQuery object for each row, so we don't + // have to create a new object inside the performance-sensitive keyup + // handler. + '$div': $option + }; } -}); + return options; +}; -Drupal.behaviors.viewsUiPreview = { - attach: function (context) { - // Only act on the edit view form. - var $contextualFiltersBucket = $(context).find('.views-display-column .views-ui-display-tab-bucket.contextual-filters'); - if ($contextualFiltersBucket.length === 0) { - return; +/** + * Keyup handler for the search box that hides or shows the relevant options. + */ +Drupal.viewsUi.OptionsSearch.prototype.handleKeyup = function (event) { + + "use strict"; + + var found, i, j, option, search, words, wordsLength, zebraClass, zebraCounter; + + // Determine the user's search query. The search text has been converted to + // lowercase. + search = this.$searchBox.val().toLowerCase(); + words = search.split(' '); + wordsLength = words.length; + + // Start the counter for restriping rows. + zebraCounter = 0; + + // Search through the search texts in the form for matching text. + var length = this.options.length; + for (i = 0; i < length; i++) { + // Use a local variable for the option being searched, for performance. + option = this.options[i]; + found = true; + // Each word in the search string has to match the item in order for the + // item to be shown. + for (j = 0; j < wordsLength; j++) { + if (option.searchText.indexOf(words[j]) === -1) { + found = false; + } } - - // If the display has no contextual filters, hide the form where you enter - // the contextual filters for the live preview. If it has contextual filters, - // show the form. - var $contextualFilters = $contextualFiltersBucket.find('.views-display-setting a'); - if ($contextualFilters.length) { - $('#preview-args').parent().show(); + if (found) { + // Show the checkbox row, and restripe it. + zebraClass = (zebraCounter % 2) ? 'odd' : 'even'; + option.$div.show(); + option.$div.removeClass('even odd'); + option.$div.addClass(zebraClass); + zebraCounter++; } else { - $('#preview-args').parent().hide(); + // The search string wasn't found; hide this item. + option.$div.hide(); } + } +}; - // Executes an initial preview. - if ($('#edit-displays-live-preview').once('edit-displays-live-preview').is(':checked')) { - $('#preview-submit').once('edit-displays-live-preview').trigger('click'); - } +Drupal.behaviors.viewsUiPreview = {}; +Drupal.behaviors.viewsUiPreview.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + + // Only act on the edit view form. + var contextualFiltersBucket = $('.views-display-column .views-ui-display-tab-bucket.contextual-filters', context); + if (contextualFiltersBucket.length === 0) { + return; + } + + // If the display has no contextual filters, hide the form where you enter + // the contextual filters for the live preview. If it has contextual filters, + // show the form. + var contextualFilters = $('.views-display-setting a', contextualFiltersBucket); + if (contextualFilters.length) { + $('#preview-args').parent().show(); + } + else { + $('#preview-args').parent().hide(); + } + + // Executes an initial preview. + if ($('#edit-displays-live-preview').once('edit-displays-live-preview').is(':checked')) { + $('#preview-submit').once('edit-displays-live-preview').click(); } }; -Drupal.behaviors.viewsUiRearrangeFilter = { - attach: function (context) { - // Only act on the rearrange filter form. - if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag['views-rearrange-filters'] === 'undefined') { - return; - } - var $context = $(context); - var $table = $context.find('#views-rearrange-filters').once('views-rearrange-filters'); - var $operator = $context.find('.form-item-filter-groups-operator').once('views-rearrange-filters'); - if ($table.length) { - new Drupal.viewsUi.RearrangeFilterHandler($table, $operator); - } +Drupal.behaviors.viewsUiRearrangeFilter = {}; +Drupal.behaviors.viewsUiRearrangeFilter.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + // Only act on the rearrange filter form. + if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag['views-rearrange-filters'] === 'undefined') { + return; + } + + var table = $('#views-rearrange-filters', context).once('views-rearrange-filters'); + var operator = $('.form-item-filter-groups-operator', context).once('views-rearrange-filters'); + if (table.length) { + new Drupal.viewsUi.rearrangeFilterHandler(table, operator); } }; /** * Improve the UI of the rearrange filters dialog box. */ -Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) { +Drupal.viewsUi.rearrangeFilterHandler = function (table, operator) { + + "use strict"; + + var $ = jQuery; // Keep a reference to the being altered and to the div containing // the filter groups operator dropdown (if it exists). - this.table = $table; - this.operator = $operator; + this.table = table; + this.operator = operator; this.hasGroupOperator = this.operator.length > 0; // Keep a reference to all draggable rows within the table. - this.draggableRows = $table.find('.draggable'); + this.draggableRows = $('.draggable', table); // Keep a reference to the buttons for adding and removing filter groups. this.addGroupButton = $('input#views-add-group'); - this.removeGroupButtons = $table.find('input.views-remove-group'); + this.removeGroupButtons = $('input.views-remove-group', table); // Add links that duplicate the functionality of the (hidden) add and remove // buttons. @@ -465,9 +527,9 @@ Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) { // next to the filters in each group, and bind a handler so that they change // based on the values of the operator dropdown within that group. this.redrawOperatorLabels(); - $table.find('.views-group-title select') + $('.views-group-title select', table) .once('views-rearrange-filter-handler') - .on('change.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); + .bind('change.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); // Bind handlers so that when a "Remove" link is clicked, we: // - Update the rowspans of cells containing an operator dropdown (since they @@ -475,92 +537,96 @@ Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) { // - Redraw the operator labels next to the filters in the group (since the // filter that is currently displayed last in each group is not supposed to // have a label display next to it). - $table.find('a.views-groups-remove-link') + $('a.views-groups-remove-link', this.table) .once('views-rearrange-filter-handler') - .on('click.views-rearrange-filter-handler', $.proxy(this, 'updateRowspans')) - .on('click.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); + .bind('click.views-rearrange-filter-handler', $.proxy(this, 'updateRowspans')) + .bind('click.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); }; -$.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, { - /** - * Insert links that allow filter groups to be added and removed. - */ - insertAddRemoveFilterGroupLinks: function () { - - // Insert a link for adding a new group at the top of the page, and make it - // match the action links styling used in a typical page.tpl.php. Note that - // Drupal does not provide a theme function for this markup, so this is the - // best we can do. - $('') - .prependTo(this.table.parent()) - // When the link is clicked, dynamically click the hidden form button for - // adding a new filter group. +/** + * Insert links that allow filter groups to be added and removed. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.insertAddRemoveFilterGroupLinks = function () { + + "use strict"; + + var $ = jQuery; + + // Insert a link for adding a new group at the top of the page, and make it + // match the action links styling used in a typical page.tpl.php. Note that + // Drupal does not provide a theme function for this markup, so this is the + // best we can do. + $('') + .prependTo(this.table.parent()) + // When the link is clicked, dynamically click the hidden form button for + // adding a new filter group. + .once('views-rearrange-filter-handler') + .bind('click.views-rearrange-filter-handler', $.proxy(this, 'clickAddGroupButton')); + + // Find each (visually hidden) button for removing a filter group and insert + // a link next to it. + var length = this.removeGroupButtons.length; + var i; + for (i = 0; i < length; i++) { + var $removeGroupButton = $(this.removeGroupButtons[i]); + var buttonId = $removeGroupButton.attr('id'); + $('' + Drupal.t('Remove group') + '') + .insertBefore($removeGroupButton) + // When the link is clicked, dynamically click the corresponding form + // button. .once('views-rearrange-filter-handler') - .on('click.views-rearrange-filter-handler', $.proxy(this, 'clickAddGroupButton')); - - // Find each (visually hidden) button for removing a filter group and insert - // a link next to it. - var length = this.removeGroupButtons.length; - var i; - for (i = 0; i < length; i++) { - var $removeGroupButton = $(this.removeGroupButtons[i]); - var buttonId = $removeGroupButton.attr('id'); - $('' + Drupal.t('Remove group') + '') - .insertBefore($removeGroupButton) - // When the link is clicked, dynamically click the corresponding form - // button. - .once('views-rearrange-filter-handler') - .on('click.views-rearrange-filter-handler', { buttonId: buttonId }, $.proxy(this, 'clickRemoveGroupButton')); - } - }, + .bind('click.views-rearrange-filter-handler', {buttonId: buttonId}, $.proxy(this, 'clickRemoveGroupButton')); + } +}; - /** - * Dynamically click the button that adds a new filter group. - */ - clickAddGroupButton: function () { - // Due to conflicts between Drupal core's AJAX system and the Views AJAX - // system, the only way to get this to work seems to be to trigger both the - // .mousedown() and .submit() events. - this.addGroupButton - .trigger('mousedown') - .trigger('submit'); - event.preventDefault(); - }, +/** + * Dynamically click the button that adds a new filter group. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.clickAddGroupButton = function () { - /** - * Dynamically click a button for removing a filter group. - * - * @param event - * Event being triggered, with event.data.buttonId set to the ID of the - * form button that should be clicked. - */ - clickRemoveGroupButton: function (event) { - // For some reason, here we only need to trigger .submit(), unlike for - // Drupal.viewsUi.RearrangeFilterHandler.prototype.clickAddGroupButton() - // where we had to trigger .mousedown() also. - this.table.find('#' + event.data.buttonId).trigger('submit'); - event.preventDefault(); - }, + "use strict"; - /** - * Move the groups operator so that it's between the first two groups, and - * duplicate it between any subsequent groups. - */ - duplicateGroupsOperator: function () { - var dropdowns, newRow, titleRow; + // Due to conflicts between Drupal core's AJAX system and the Views AJAX + // system, the only way to get this to work seems to be to trigger both the + // .mousedown() and .submit() events. + this.addGroupButton.mousedown(); + this.addGroupButton.submit(); + event.preventDefault(); +}; + +/** + * Dynamically click a button for removing a filter group. + * + * @param event + * Event being triggered, with event.data.buttonId set to the ID of the + * form button that should be clicked. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.clickRemoveGroupButton = function (event) { + + "use strict"; - var titleRows = $('tr.views-group-title'); + // For some reason, here we only need to trigger .submit(), unlike for + // Drupal.viewsUi.rearrangeFilterHandler.prototype.clickAddGroupButton() + // where we had to trigger .mousedown() also. + jQuery('input#' + event.data.buttonId, this.table).submit(); + event.preventDefault(); +}; - // Get rid of the explanatory text around the operator; its placement is - // explanatory enough. - this.operator.find('label').add('div.description').addClass('element-invisible'); - this.operator.find('select').addClass('form-select'); +/** + * Move the groups operator so that it's between the first two groups, and + * duplicate it between any subsequent groups. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.duplicateGroupsOperator = function () { - // Keep a list of the operator dropdowns, so we can sync their behavior later. - dropdowns = this.operator; + "use strict"; - // Move the operator to a new row just above the second group. - titleRow = $('tr#views-group-title-2'); + var $ = jQuery; + var dropdowns, newRow; + + var titleRows = $('tr.views-group-title'), titleRow; + + // Get rid of the explanatory text around the operator; its placement is + // explanatory enough. this.operator.find('label').add('div.description').addClass('visually-hidden'); this.operator.find('select').addClass('form-select'); @@ -581,231 +647,261 @@ $.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, { var fakeOperator = this.operator.clone(); fakeOperator.attr('id', ''); newRow = $(''); - newRow.find('td').append(fakeOperator); - newRow.insertBefore(titleRow); - dropdowns = dropdowns.add(fakeOperator); - } + newRow.find('td').append(fakeOperator); + newRow.insertBefore(titleRow); + dropdowns = dropdowns.add(fakeOperator); + } - return dropdowns; - }, + return dropdowns; +}; - /** - * Make the duplicated groups operators change in sync with each other. - */ - syncGroupsOperators: function () { - if (this.dropdowns.length < 2) { - // We only have one dropdown (or none at all), so there's nothing to sync. - return; - } +/** + * Make the duplicated groups operators change in sync with each other. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.syncGroupsOperators = function () { - this.dropdowns.on('change', $.proxy(this, 'operatorChangeHandler')); - }, + "use strict"; + + if (this.dropdowns.length < 2) { + // We only have one dropdown (or none at all), so there's nothing to sync. + return; + } + + this.dropdowns.change(jQuery.proxy(this, 'operatorChangeHandler')); +}; + +/** + * Click handler for the operators that appear between filter groups. + * + * Forces all operator dropdowns to have the same value. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.operatorChangeHandler = function (event) { + + "use strict"; + + var $ = jQuery; + var $target = $(event.target); + var operators = this.dropdowns.find('select').not($target); + + // Change the other operators to match this new value. + operators.val($target.val()); +}; + +Drupal.viewsUi.rearrangeFilterHandler.prototype.modifyTableDrag = function () { + + "use strict"; + + var tableDrag = Drupal.tableDrag['views-rearrange-filters']; + var filterHandler = this; /** - * Click handler for the operators that appear between filter groups. + * Override the row.onSwap method from tabledrag.js. * - * Forces all operator dropdowns to have the same value. + * When a row is dragged to another place in the table, several things need + * to occur. + * - The row needs to be moved so that it's within one of the filter groups. + * - The operator cells that span multiple rows need their rowspan attributes + * updated to reflect the number of rows in each group. + * - The operator labels that are displayed next to each filter need to be + * redrawn, to account for the row's new location. */ - operatorChangeHandler: function (event) { - var $target = $(event.target); - var operators = this.dropdowns.find('select').not($target); - - // Change the other operators to match this new value. - operators.val($target.val()); - }, - - modifyTableDrag: function () { - var tableDrag = Drupal.tableDrag['views-rearrange-filters']; - var filterHandler = this; - - /** - * Override the row.onSwap method from tabledrag.js. - * - * When a row is dragged to another place in the table, several things need - * to occur. - * - The row needs to be moved so that it's within one of the filter groups. - * - The operator cells that span multiple rows need their rowspan attributes - * updated to reflect the number of rows in each group. - * - The operator labels that are displayed next to each filter need to be - * redrawn, to account for the row's new location. - */ - tableDrag.row.prototype.onSwap = function () { - if (filterHandler.hasGroupOperator) { - // Make sure the row that just got moved (this.group) is inside one of - // the filter groups (i.e. below an empty marker row or a draggable). If - // it isn't, move it down one. - var thisRow = $(this.group); - var previousRow = thisRow.prev('tr'); - if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) { - // Move the dragged row down one. - var next = thisRow.next(); - if (next.is('tr')) { - this.swap('after', next); - } + tableDrag.row.prototype.onSwap = function () { + if (filterHandler.hasGroupOperator) { + // Make sure the row that just got moved (this.group) is inside one of + // the filter groups (i.e. below an empty marker row or a draggable). If + // it isn't, move it down one. + var thisRow = jQuery(this.group); + var previousRow = thisRow.prev('tr'); + if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) { + // Move the dragged row down one. + var next = thisRow.next(); + if (next.is('tr')) { + this.swap('after', next); } - filterHandler.updateRowspans(); } - // Redraw the operator labels that are displayed next to each filter, to - // account for the row's new location. - filterHandler.redrawOperatorLabels(); - }; + filterHandler.updateRowspans(); + } + // Redraw the operator labels that are displayed next to each filter, to + // account for the row's new location. + filterHandler.redrawOperatorLabels(); + }; - /** - * Override the onDrop method from tabledrag.js. - */ - tableDrag.onDrop = function () { - // If the tabledrag change marker (i.e., the "*") has been inserted inside - // a row after the operator label (i.e., "And" or "Or") rearrange the items - // so the operator label continues to appear last. - var changeMarker = $(this.oldRowElement).find('.tabledrag-changed'); - if (changeMarker.length) { - // Search for occurrences of the operator label before the change marker, - // and reverse them. - var operatorLabel = changeMarker.prevAll('.views-operator-label'); - if (operatorLabel.length) { - operatorLabel.insertAfter(changeMarker); - } + /** + * Override the onDrop method from tabledrag.js. + */ + tableDrag.onDrop = function () { + var $ = jQuery; + + // If the tabledrag change marker (i.e., the "*") has been inserted inside + // a row after the operator label (i.e., "And" or "Or") rearrange the items + // so the operator label continues to appear last. + var changeMarker = $(this.oldRowElement).find('.tabledrag-changed'); + if (changeMarker.length) { + // Search for occurrences of the operator label before the change marker, + // and reverse them. + var operatorLabel = changeMarker.prevAll('.views-operator-label'); + if (operatorLabel.length) { + operatorLabel.insertAfter(changeMarker); } + } - // Make sure the "group" dropdown is properly updated when rows are dragged - // into an empty filter group. This is borrowed heavily from the block.js - // implementation of tableDrag.onDrop(). - var groupRow = $(this.rowObject.element).prevAll('tr.group-message').get(0); - var groupName = groupRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); - var groupField = $('select.views-group-select', this.rowObject.element); - if ($(this.rowObject.element).prev('tr').is('.group-message') && !groupField.is('.views-group-select-' + groupName)) { - var oldGroupName = groupField.attr('class').replace(/([^ ]+[ ]+)*views-group-select-([^ ]+)([ ]+[^ ]+)*/, '$2'); - groupField.removeClass('views-group-select-' + oldGroupName).addClass('views-group-select-' + groupName); - groupField.val(groupName); - } - }; - }, + // Make sure the "group" dropdown is properly updated when rows are dragged + // into an empty filter group. This is borrowed heavily from the block.js + // implementation of tableDrag.onDrop(). + var groupRow = $(this.rowObject.element).prevAll('tr.group-message').get(0); + var groupName = groupRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); + var groupField = $('select.views-group-select', this.rowObject.element); + if ($(this.rowObject.element).prev('tr').is('.group-message') && !groupField.is('.views-group-select-' + groupName)) { + var oldGroupName = groupField.attr('class').replace(/([^ ]+[ ]+)*views-group-select-([^ ]+)([ ]+[^ ]+)*/, '$2'); + groupField.removeClass('views-group-select-' + oldGroupName).addClass('views-group-select-' + groupName); + groupField.val(groupName); + } + }; +}; - /** - * Redraw the operator labels that are displayed next to each filter. - */ - redrawOperatorLabels: function () { - for (var i = 0; i < this.draggableRows.length; i++) { - // Within the row, the operator labels are displayed inside the first table - // cell (next to the filter name). - var $draggableRow = $(this.draggableRows[i]); - var $firstCell = $draggableRow.find('td:first'); - if ($firstCell.length) { - // The value of the operator label ("And" or "Or") is taken from the - // first operator dropdown we encounter, going backwards from the current - // row. This dropdown is the one associated with the current row's filter - // group. - var operatorValue = $draggableRow.prevAll('.views-group-title').find('option:selected').html(); - var operatorLabel = '' + operatorValue + ''; - // If the next visible row after this one is a draggable filter row, - // display the operator label next to the current row. (Checking for - // visibility is necessary here since the "Remove" links hide the removed - // row but don't actually remove it from the document). - var $nextRow = $draggableRow.nextAll(':visible').eq(0); - var $existingOperatorLabel = $firstCell.find('.views-operator-label'); - if ($nextRow.hasClass('draggable')) { - // If an operator label was already there, replace it with the new one. - if ($existingOperatorLabel.length) { - $existingOperatorLabel.replaceWith(operatorLabel); - } - // Otherwise, append the operator label to the end of the table cell. - else { - $firstCell.append(operatorLabel); - } +/** + * Redraw the operator labels that are displayed next to each filter. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.redrawOperatorLabels = function () { + + "use strict"; + + var $ = jQuery; + var i; + for (i = 0; i < this.draggableRows.length; i++) { + // Within the row, the operator labels are displayed inside the first table + // cell (next to the filter name). + var $draggableRow = $(this.draggableRows[i]); + var $firstCell = $('td:first', $draggableRow); + if ($firstCell.length) { + // The value of the operator label ("And" or "Or") is taken from the + // first operator dropdown we encounter, going backwards from the current + // row. This dropdown is the one associated with the current row's filter + // group. + var operatorValue = $draggableRow.prevAll('.views-group-title').find('option:selected').html(); + var operatorLabel = '' + operatorValue + ''; + // If the next visible row after this one is a draggable filter row, + // display the operator label next to the current row. (Checking for + // visibility is necessary here since the "Remove" links hide the removed + // row but don't actually remove it from the document). + var $nextRow = $draggableRow.nextAll(':visible').eq(0); + var $existingOperatorLabel = $firstCell.find('.views-operator-label'); + if ($nextRow.hasClass('draggable')) { + // If an operator label was already there, replace it with the new one. + if ($existingOperatorLabel.length) { + $existingOperatorLabel.replaceWith(operatorLabel); } - // If the next row doesn't contain a filter, then this is the last row - // in the group. We don't want to display the operator there (since - // operators should only display between two related filters, e.g. - // "filter1 AND filter2 AND filter3"). So we remove any existing label - // that this row has. + // Otherwise, append the operator label to the end of the table cell. else { - $existingOperatorLabel.remove(); + $firstCell.append(operatorLabel); } } + // If the next row doesn't contain a filter, then this is the last row + // in the group. We don't want to display the operator there (since + // operators should only display between two related filters, e.g. + // "filter1 AND filter2 AND filter3"). So we remove any existing label + // that this row has. + else { + $existingOperatorLabel.remove(); + } } - }, + } +}; - /** - * Update the rowspan attribute of each cell containing an operator dropdown. - */ - updateRowspans: function () { - var i, $row, $currentEmptyRow, draggableCount, $operatorCell; - var rows = $(this.table).find('tr'); - var length = rows.length; - for (i = 0; i < length; i++) { - $row = $(rows[i]); - if ($row.hasClass('views-group-title')) { - // This row is a title row. - // Keep a reference to the cell containing the dropdown operator. - $operatorCell = $row.find('td.group-operator'); - // Assume this filter group is empty, until we find otherwise. - draggableCount = 0; - $currentEmptyRow = $row.next('tr'); - $currentEmptyRow.removeClass('group-populated').addClass('group-empty'); - // The cell with the dropdown operator should span the title row and - // the "this group is empty" row. - $operatorCell.attr('rowspan', 2); - } - else if ($row.hasClass('draggable') && $row.is(':visible')) { - // We've found a visible filter row, so we now know the group isn't empty. - draggableCount++; - $currentEmptyRow.removeClass('group-empty').addClass('group-populated'); - // The operator cell should span all draggable rows, plus the title. - $operatorCell.attr('rowspan', draggableCount + 1); - } +/** + * Update the rowspan attribute of each cell containing an operator dropdown. + */ +Drupal.viewsUi.rearrangeFilterHandler.prototype.updateRowspans = function () { + + "use strict"; + + var $ = jQuery; + var i, $row, $currentEmptyRow, draggableCount, $operatorCell; + var rows = $(this.table).find('tr'); + var length = rows.length; + for (i = 0; i < length; i++) { + $row = $(rows[i]); + if ($row.hasClass('views-group-title')) { + // This row is a title row. + // Keep a reference to the cell containing the dropdown operator. + $operatorCell = $($row.find('td.group-operator')); + // Assume this filter group is empty, until we find otherwise. + draggableCount = 0; + $currentEmptyRow = $row.next('tr'); + $currentEmptyRow.removeClass('group-populated').addClass('group-empty'); + // The cell with the dropdown operator should span the title row and + // the "this group is empty" row. + $operatorCell.attr('rowspan', 2); } - }}); + else if (($row).hasClass('draggable') && $row.is(':visible')) { + // We've found a visible filter row, so we now know the group isn't empty. + draggableCount++; + $currentEmptyRow.removeClass('group-empty').addClass('group-populated'); + // The operator cell should span all draggable rows, plus the title. + $operatorCell.attr('rowspan', draggableCount + 1); + } + } +}; +Drupal.behaviors.viewsFilterConfigSelectAll = {}; /** * Add a select all checkbox, which checks each checkbox at once. */ -Drupal.behaviors.viewsFilterConfigSelectAll = { - attach: function (context) { - // Show the select all checkbox. - $(context).find('#views-ui-config-item-form div.form-item-options-value-all').once('filterConfigSelectAll') - .show() - .find('input[type=checkbox]') - .on('click', function () { - var checked = $(this).is(':checked'); - // Update all checkbox beside the select all checkbox. - $(this).parents('.form-checkboxes').find('input[type=checkbox]').each(function () { - $(this).attr('checked', checked); - }); - }); - // Uncheck the select all checkbox if any of the others are unchecked. - $('#views-ui-config-item-form').find('div.form-type-checkbox').not($('.form-item-options-value-all')) - .find('input[type=checkbox]') - .on('click', function () { - if ($(this).is('checked') === false) { - $('#edit-options-value-all').prop('checked', false); - } - }); - } +Drupal.behaviors.viewsFilterConfigSelectAll.attach = function(context) { + + "use strict"; + + var $ = jQuery; + // Show the select all checkbox. + $('#views-ui-config-item-form div.form-item-options-value-all', context).once(function() { + $(this).show(); + }) + .find('input[type=checkbox]') + .click(function() { + var checked = $(this).is(':checked'); + // Update all checkbox beside the select all checkbox. + $(this).parents('.form-checkboxes').find('input[type=checkbox]').each(function() { + $(this).attr('checked', checked); + }); + }); + // Uncheck the select all checkbox if any of the others are unchecked. + $('#views-ui-config-item-form div.form-type-checkbox').not($('.form-item-options-value-all')).find('input[type=checkbox]').each(function() { + $(this).click(function() { + if ($(this).is('checked') === false) { + $('#edit-options-value-all').prop('checked', false); + } + }); + }); }; /** * Remove icon class from elements that are themed as buttons or dropbuttons. */ -Drupal.behaviors.viewsRemoveIconClass = { - attach: function (context) { - $(context).find('.dropbutton').once('dropbutton-icon', function () { - $(this).find('.icon').removeClass('icon'); - }); - } +Drupal.behaviors.viewsRemoveIconClass = {}; +Drupal.behaviors.viewsRemoveIconClass.attach = function (context, settings) { + + "use strict"; + + jQuery(context).find('.dropbutton').once('dropbutton-icon', function () { + jQuery(this).find('.icon').removeClass('icon'); + }); }; /** * Change "Expose filter" buttons into checkboxes. */ -Drupal.behaviors.viewsUiCheckboxify = { - attach: function (context, settings) { - var $buttons = $('#edit-options-expose-button-button, #edit-options-group-button-button').once('views-ui-checkboxify'); - var length = $buttons.length; - var i; - for (i = 0; i < length; i++) { - new Drupal.viewsUi.Checkboxifier($buttons[i]); - } +Drupal.behaviors.viewsUiCheckboxify = {}; +Drupal.behaviors.viewsUiCheckboxify.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + var $buttons = $('#edit-options-expose-button-button, #edit-options-group-button-button').once('views-ui-checkboxify'); + var length = $buttons.length; + var i; + for (i = 0; i < length; i++) { + new Drupal.viewsUi.Checkboxifier($buttons[i]); } }; @@ -813,26 +909,30 @@ Drupal.behaviors.viewsUiCheckboxify = { * Change the default widget to select the default group according to the * selected widget for the exposed group. */ -Drupal.behaviors.viewsUiChangeDefaultWidget = { - attach: function () { - function changeDefaultWidget (event) { - if ($(event.target).prop('checked')) { - $('input.default-radios').hide(); - $('td.any-default-radios-row').parent().hide(); - $('input.default-checkboxes').show(); - } - else { - $('input.default-checkboxes').hide(); - $('td.any-default-radios-row').parent().show(); - $('input.default-radios').show(); - } +Drupal.behaviors.viewsUiChangeDefaultWidget = {}; +Drupal.behaviors.viewsUiChangeDefaultWidget.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + function change_default_widget(multiple) { + if (multiple) { + $('input.default-radios').hide(); + $('td.any-default-radios-row').parent().hide(); + $('input.default-checkboxes').show(); + } + else { + $('input.default-checkboxes').hide(); + $('td.any-default-radios-row').parent().show(); + $('input.default-radios').show(); } - // Update on widget change. - $('input[name="options[group_info][multiple]"]') - .on('change', changeDefaultWidget) - // Update the first time the form is rendered. - .trigger('change'); } + // Update on widget change. + $('input[name="options[group_info][multiple]"]').change(function() { + change_default_widget($(this).attr("checked")); + }); + // Update the first time the form is rendered. + $('input[name="options[group_info][multiple]"]').trigger('change'); }; /** @@ -842,6 +942,10 @@ Drupal.behaviors.viewsUiChangeDefaultWidget = { * The DOM object representing the button to be checkboxified. */ Drupal.viewsUi.Checkboxifier = function (button) { + + "use strict"; + + var $ = jQuery; this.$button = $(button); this.$parent = this.$button.parent('div.views-expose, div.views-grouped'); this.$input = this.$parent.find('input:checkbox, input:radio'); @@ -857,6 +961,9 @@ Drupal.viewsUi.Checkboxifier = function (button) { * When the checkbox is checked or unchecked, simulate a button press. */ Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) { + + "use strict"; + this.$button.mousedown(); this.$button.submit(); }; @@ -864,48 +971,52 @@ Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) { /** * Change the Apply button text based upon the override select state. */ -Drupal.behaviors.viewsUiOverrideSelect = { - attach: function (context) { - $(context).find('#edit-override-dropdown').once('views-ui-override-button-text', function () { - // Closures! :( - var $submit = $('#edit-submit'); - var old_value = $submit.val(); - - $submit.once('views-ui-override-button-text') - .on('mouseup', function () { - $(this).val(old_value); - return true; - }); - - $(this).on('change', function () { - var $this = $(this); - if ($this.val() === 'default') { - $submit.val(Drupal.t('Apply (all displays)')); - } - else if ($this.val() === 'default_revert') { - $submit.val(Drupal.t('Revert to default')); - } - else { - $submit.val(Drupal.t('Apply (this display)')); - } - }) - .trigger('change'); - }); +Drupal.behaviors.viewsUiOverrideSelect = {}; +Drupal.behaviors.viewsUiOverrideSelect.attach = function (context, settings) { + + "use strict"; + + var $ = jQuery; + $('#edit-override-dropdown', context).once('views-ui-override-button-text', function() { + // Closures! :( + var $submit = $('#edit-submit', context); + var old_value = $submit.val(); + + $submit.once('views-ui-override-button-text') + .bind('mouseup', function() { + $(this).val(old_value); + return true; + }); + + $(this).bind('change', function() { + if ($(this).val() === 'default') { + $submit.val(Drupal.t('Apply (all displays)')); + } + else if ($(this).val() === 'default_revert') { + $submit.val(Drupal.t('Revert to default')); + } + else { + $submit.val(Drupal.t('Apply (this display)')); + } + }) + .trigger('change'); + }); - } }; Drupal.viewsUi.resizeModal = function (e, no_shrink) { + + "use strict"; + + var $ = jQuery; var $modal = $('.views-ui-dialog'); - var $window = $(window); - var windowWidth = $window.width(); - var $scroll = $modal.find('.scroll'); + var $scroll = $('.scroll', $modal); if ($modal.size() === 0 || $modal.css('display') === 'none') { return; } - var maxWidth = parseInt(windowWidth * 0.85, 10); // 85% of window - var minWidth = parseInt(windowWidth * 0.6, 10); // 60% of window + var maxWidth = parseInt($(window).width() * .85); // 70% of window + var minWidth = parseInt($(window).width() * .6); // 70% of window // Set the modal to the minwidth so that our width calculation of // children works. @@ -913,7 +1024,7 @@ Drupal.viewsUi.resizeModal = function (e, no_shrink) { var width = minWidth; // Don't let the window get more than 80% of the display high. - var maxHeight = parseInt($window.height() * 0.8, 10); + var maxHeight = parseInt($(window).height() * .8); var minHeight = 200; if (no_shrink) { minHeight = $modal.height(); @@ -928,10 +1039,10 @@ Drupal.viewsUi.resizeModal = function (e, no_shrink) { // Calculate the height of the 'scroll' region. var scrollHeight = 0; - scrollHeight += parseInt($scroll.css('padding-top'), 10); - scrollHeight += parseInt($scroll.css('padding-bottom'), 10); + scrollHeight += parseInt($scroll.css('padding-top')); + scrollHeight += parseInt($scroll.css('padding-bottom')); - $scroll.children().each(function () { + $scroll.children().each(function() { var w = $(this).innerWidth(); if (w > width) { width = w; @@ -943,8 +1054,8 @@ Drupal.viewsUi.resizeModal = function (e, no_shrink) { // will be. var difference = 0; - difference += parseInt($scroll.css('padding-top'), 10); - difference += parseInt($scroll.css('padding-bottom'), 10); + difference += parseInt($scroll.css('padding-top')); + difference += parseInt($scroll.css('padding-bottom')); difference += $('.views-override').outerHeight(true); difference += $('.views-messages').outerHeight(true); difference += $('#views-ajax-title').outerHeight(true); @@ -987,29 +1098,34 @@ Drupal.viewsUi.resizeModal = function (e, no_shrink) { }; -Drupal.behaviors.viewsUiHandlerRemoveLink = { - attach: function (context) { - var $context = $(context); - // Handle handler deletion by looking for the hidden checkbox and hiding the - // row. - $context.find('a.views-remove-link').once('views').on('click', function (event) { - var id = $(this).attr('id').replace('views-remove-link-', ''); - $context.find('#views-row-' + id).hide(); - $context.find('#views-removed-' + id).prop('checked', true); - event.preventDefault(); - }); +Drupal.behaviors.viewsUiHandlerRemoveLink = {}; +Drupal.behaviors.viewsUiHandlerRemoveLink.attach = function(context) { + var $ = jQuery; + + // Handle handler deletion by looking for the hidden checkbox and hiding the + // row. + $('a.views-remove-link', context).once('views-processed').click(function(event) { + var id = $(this).attr('id').replace('views-remove-link-', ''); + $('#views-row-' + id, context).hide(); + $('#views-removed-' + id, context).attr('checked', true); + event.preventDefault(); + }); + + // Handle display deletion by looking for the hidden checkbox and hiding the + // row. + $('a.display-remove-link', context).once('display').click(function(event) { + var id = $(this).attr('id').replace('display-remove-link-', ''); + $('#display-row-' + id, context).hide(); + $('#display-removed-' + id, context).attr('checked', true); + event.preventDefault(); + }); - // Handle display deletion by looking for the hidden checkbox and hiding the - // row. - $context.find('a.display-remove-link').once('display').on('click', function (event) { - var id = $(this).attr('id').replace('display-remove-link-', ''); - $context.find('#display-row-' + id).hide(); - $context.find('#display-removed-' + id).prop('checked', true); - event.preventDefault(); - }); - } }; -$(window).on('resize scroll', debounce(Drupal.viewsUi.resizeModal, 100)); +jQuery(function() { -})(jQuery, Drupal, drupalSettings, Drupal.debounce); + "use strict" + + jQuery(window).bind('resize', Drupal.viewsUi.resizeModal); + jQuery(window).bind('scroll', Drupal.viewsUi.resizeModal); +}); diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Tests/OverrideDisplaysTest.php b/core/modules/views_ui/lib/Drupal/views_ui/Tests/OverrideDisplaysTest.php index 1dab1f6..38b3d36 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Tests/OverrideDisplaysTest.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/OverrideDisplaysTest.php @@ -54,7 +54,7 @@ function testOverrideDisplays() { $this->assertText($original_title); // Confirm that the view block is available in the block administration UI. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertText('View: ' . $view['label']); // Place the block. @@ -112,7 +112,7 @@ function testWizardMixedDefaultOverriddenDisplays() { $this->assertNoText($view['block[title]']); // Confirm that the block is available in the block administration UI. - $this->drupalGet('admin/structure/block/list/' . config('system.theme')->get('default') . '/add'); + $this->drupalGet('admin/structure/block/list/block_plugin_ui:' . config('system.theme')->get('default') . '/add'); $this->assertText('View: ' . $view['label']); // Put the block into the first sidebar region, and make sure it will not diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module index 02ee998..295c805 100644 --- a/core/modules/views_ui/views_ui.module +++ b/core/modules/views_ui/views_ui.module @@ -201,7 +201,6 @@ function views_ui_library_info() { array('system', 'jquery'), array('system', 'drupal'), array('system', 'drupalSettings'), - array('system', 'drupal.debounce'), array('system', 'jquery.once'), array('system', 'jquery.form'), array('system', 'drupal.ajax'), diff --git a/core/tests/Drupal/Tests/Component/Utility/SortArrayTest.php b/core/tests/Drupal/Tests/Component/Utility/SortArrayTest.php deleted file mode 100644 index 07a493b..0000000 --- a/core/tests/Drupal/Tests/Component/Utility/SortArrayTest.php +++ /dev/null @@ -1,326 +0,0 @@ - 'SortArray test', - 'description' => 'Test that the SortArray functions work properly.', - 'group' => 'Common', - ); - } - - /** - * Tests SortArray::sortByWeightElement() input against expected output. - * - * @dataProvider providerSortByWeightElement - * - * @param array $a - * The first input array for the SortArray::sortByWeightElement() method. - * @param array $b - * The second input array for the SortArray::sortByWeightElement(). - * @param integer $expected - * The expected output from calling the method. - * - * @see Drupal\Component\Utility\SortArray::sortByWeightElement() - * @see Drupal\Tests\Component\Utility\SortArrayTest::providersortByWeightElement() - */ - public function testSortByWeightElement($a, $b, $expected) { - $result = SortArray::sortByWeightElement($a, $b); - $this->assertEquals($expected, $result); - } - - /** - * Data provider for SortArray::sortByWeightElement(). - * - * @return array - * An array of tests, matching the parameter inputs for - * testSortByWeightElement. - * - * @see \Drupal\Component\Utility\SortArray::sortByWeightElement() - * @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByWeightElement() - */ - public function providerSortByWeightElement() { - $tests = array(); - - // Weights set and equal. - $tests[] = array( - array('weight' => 1), - array('weight' => 1), - 0 - ); - - // Weights set and $a is less (lighter) than $b. - $tests[] = array( - array('weight' => 1), - array('weight' => 2), - -1 - ); - - // Weights set and $a is greater (heavier) than $b. - $tests[] = array( - array('weight' => 2), - array('weight' => 1), - 1 - ); - - // Weights not set. - $tests[] = array( - array(), - array(), - 0 - ); - - // Weights for $b not set. - $tests[] = array( - array('weight' => 1), - array(), - 1 - ); - - // Weights for $a not set. - $tests[] = array( - array(), - array('weight' => 1), - -1 - ); - - return $tests; - } - - /** - * Tests SortArray::sortByWeightProperty() input against expected output. - * - * @dataProvider providerSortByWeightProperty - * - * @param array $a - * The first input array for the SortArray::sortByWeightProperty() method. - * @param array $b - * The second input array for the SortArray::sortByWeightProperty(). - * @param integer $expected - * The expected output from calling the method. - * - * @see Drupal\Component\Utility\SortArray::sortByWeightProperty() - * @see Drupal\Tests\Component\Utility\SortArrayTest::SortByWeightProperty() - */ - public function testSortByWeightProperty($a, $b, $expected) { - $result = SortArray::sortByWeightProperty($a, $b); - $this->assertEquals($expected, $result); - } - - /** - * Data provider for SortArray::sortByWeightProperty(). - * - * @return array - * An array of tests, matching the parameter inputs for - * testSortByWeightProperty. - * - * @see \Drupal\Component\Utility\SortArray::sortByWeightProperty() - * @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByWeightProperty() - */ - public function providerSortByWeightProperty() { - $tests = array(); - - // Weights set and equal. - $tests[] = array( - array('#weight' => 1), - array('#weight' => 1), - 0 - ); - - // Weights set and $a is less (lighter) than $b. - $tests[] = array( - array('#weight' => 1), - array('#weight' => 2), - -1 - ); - - // Weights set and $a is greater (heavier) than $b. - $tests[] = array( - array('#weight' => 2), - array('#weight' => 1), - 1 - ); - - // Weights not set. - $tests[] = array( - array(), - array(), - 0 - ); - - // Weights for $b not set. - $tests[] = array( - array('#weight' => 1), - array(), - 1 - ); - - // Weights for $a not set. - $tests[] = array( - array(), - array('#weight' => 1), - -1 - ); - - return $tests; - } - - /** - * Tests SortArray::sortByTitleElement() input against expected output. - * - * @dataProvider providerSortByTitleElement - * - * @param array $a - * The first input item for comparison. - * @param array $b - * The second item for comparison. - * @param integer $expected - * The expected output from calling the method. - * - * @see Drupal\Component\Utility\SortArray::sortByTitleElement() - * @see Drupal\Tests\Component\Utility\SortArrayTest::providerSortByTitleElement() - */ - public function testSortByTitleElement($a, $b, $expected) { - $result = SortArray::sortByTitleElement($a, $b); - $this->assertEquals($expected, $result); - } - - /** - * Data provider for SortArray::sortByTitleElement(). - * - * @return array - * An array of tests, matching the parameter inputs for - * testSortByTitleElement. - * - * @see \Drupal\Component\Utility\SortArray::sortByTitleElement() - * @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByTitleElement() - */ - public function providerSortByTitleElement() { - $tests = array(); - - // Titles set and equal. - $tests[] = array( - array('title' => 'test'), - array('title' => 'test'), - 0 - ); - - // Title $a not set. - $tests[] = array( - array(), - array('title' => 'test'), - -4 - ); - - // Title $b not set. - $tests[] = array( - array('title' => 'test'), - array(), - 4 - ); - - // Titles set but not equal. - $tests[] = array( - array('title' => 'test'), - array('title' => 'testing'), - -1 - ); - - // Titles set but not equal. - $tests[] = array( - array('title' => 'testing'), - array('title' => 'test'), - 1 - ); - - return $tests; - } - - /** - * Tests SortArray::sortByTitleProperty() input against expected output. - * - * @dataProvider providerSortByTitleProperty - * - * @param array $a - * The first input item for comparison. - * @param array $b - * The second item for comparison. - * @param integer $expected - * The expected output from calling the method. - * - * @see Drupal\Component\Utility\SortArray::sortByTitleProperty() - * @see Drupal\Tests\Component\Utility\SortArrayTest::SortByTitleProperty() - */ - public function testSortByTitleProperty($a, $b, $expected) { - $result = SortArray::sortByTitleProperty($a, $b); - $this->assertEquals($expected, $result); - } - - /** - * Data provider for SortArray::sortByTitleProperty(). - * - * @return array - * An array of tests, matching the parameter inputs for - * testSortByTitleProperty. - * - * @see \Drupal\Component\Utility\SortArray::sortByTitleProperty() - * @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByTitleProperty() - */ - public function providerSortByTitleProperty() { - $tests = array(); - - // Titles set and equal. - $tests[] = array( - array('#title' => 'test'), - array('#title' => 'test'), - 0 - ); - - // Title $a not set. - $tests[] = array( - array(), - array('#title' => 'test'), - -4 - ); - - // Title $b not set. - $tests[] = array( - array('#title' => 'test'), - array(), - 4 - ); - - // Titles set but not equal. - $tests[] = array( - array('#title' => 'test'), - array('#title' => 'testing'), - -1 - ); - - // Titles set but not equal. - $tests[] = array( - array('#title' => 'testing'), - array('#title' => 'test'), - 1 - ); - - return $tests; - } - -} diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php deleted file mode 100644 index ead80b5..0000000 --- a/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php +++ /dev/null @@ -1,148 +0,0 @@ - 'Entity list controller test', - 'description' => 'Unit test of entity access checking system.', - 'group' => 'Entity' - ); - } - - /** - * Entity info used by the test. - * - * @var array - * - * @see entity_get_info() - */ - public static $entityInfo = array( - 'entity_keys' => array( - 'id' => 'id', - 'label' => 'label', - ), - 'config_prefix' => 'user.role', - 'class' => 'Drupal\user\Plugin\Core\Entity\Role', - ); - - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->role = $this - ->getMockBuilder('Drupal\user\Plugin\Core\Entity\Role') - ->setConstructorArgs(array('entityInfo' => static::$entityInfo, 'user_role')) - ->getMock(); - - // Creates a stub role storage controller and replace the buildOperations() - // method with an empty version, because buildOperations() relies on hooks. - $role_storage_controller = $this->getMockBuilder('Drupal\user\RoleStorageController') - ->disableOriginalConstructor() - ->getMock(); - - $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler') - ->disableOriginalConstructor() - ->getMock(); - - $this->entityListController = $this->getMock('Drupal\Core\Entity\EntityListController', array('buildOperations'), array('user_role', static::$entityInfo, $role_storage_controller, $module_handler)); - - $this->entityListController->expects($this->any()) - ->method('buildOperations') - ->will($this->returnValue(array())); - - } - - /** - * Tests that buildRow() returns a string which has been run through - * String::checkPlain(). - * - * @dataProvider providerTestBuildRow - * - * @param string $input - * The entity label being passed into buildRow. - * @param string $expected - * The expected output of the label from buildRow. - * @param string $message - * The message to provide as output for the test. - * @param bool $ignorewarnings - * Whether or not to ignore PHP 5.3+ invalid multibyte sequence warnings. - * - * @see \Drupal\Component\Utility\Drupal\Core\Entity\EntityListController::buildRow() - */ - public function testBuildRow($input, $expected, $message, $ignorewarnings = FALSE) { - $this->role->expects($this->any()) - ->method('label') - ->will($this->returnValue($input)); - - if ($ignorewarnings) { - $built_row = @$this->entityListController->buildRow($this->role); - } - else { - $built_row = $this->entityListController->buildRow($this->role); - } - - $this->assertEquals($built_row['label'], $expected, $message); - } - - /** - * Data provider for testBuildRow(). - * - * @see self::testBuildRow() - * @see \Drupal\Tests\Component\Utility\StringTest::providerCheckPlain() - * - * @return array - * An array containing a string, the expected return from - * String::checkPlain, a message to be output for failures, and whether the - * test should be processed as multibyte. - */ - public function providerTestBuildRow() { - $tests = array(); - // Checks that invalid multi-byte sequences are rejected. - $tests[] = array("Foo\xC0barbaz", '', 'EntityListController::buildRow() rejects invalid sequence "Foo\xC0barbaz"', TRUE); - $tests[] = array("\xc2\"", '', 'EntityListController::buildRow() rejects invalid sequence "\xc2\""', TRUE); - $tests[] = array("Fooÿñ", "Fooÿñ", 'EntityListController::buildRow() accepts valid sequence "Fooÿñ"'); - - // Checks that special characters are escaped. - $tests[] = array("