'table', * '#header' => $header, * '#rows' => $rows, * '#attributes' => array( * 'id' => 'my-module-table', * ), * ); * return \Drupal::service('renderer')->render($table); * @endcode * * In the theme function for the form, a special class must be added to each * form element within the same column, "grouping" them together. * * In a situation where a single weight column is being sorted in the table, the * classes could be added like this (in the theme function): * @code * $form['my_elements'][$delta]['weight']['#attributes']['class'] = array('my-elements-weight'); * @endcode * * Each row of the table must also have a class of "draggable" in order to * enable the drag handles: * @code * $row = array(...); * $rows[] = array( * 'data' => $row, * 'class' => array('draggable'), * ); * @endcode * * When tree relationships are present, the two additional classes * 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior: * - Rows with the 'tabledrag-leaf' class cannot have child rows. * - Rows with the 'tabledrag-root' class cannot be nested under a parent row. * * Calling drupal_attach_tabledrag() would then be written as such: * @code * drupal_attach_tabledrag('my-module-table', array( * 'action' => 'order', * 'relationship' => 'sibling', * 'group' => 'my-elements-weight', * ); * @endcode * * In a more complex case where there are several groups in one column (such as * the block regions on the admin/structure/block page), a separate subgroup * class must also be added to differentiate the groups. * @code * $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('my-elements-weight', 'my-elements-weight-' . $region); * @endcode * * The 'group' option is still 'my-element-weight', and the additional * 'subgroup' option will be passed in as 'my-elements-weight-' . $region. This * also means that you'll need to call drupal_attach_tabledrag() once for every * region added. * * @code * foreach ($regions as $region) { * drupal_attach_tabledrag('my-module-table', array( * 'action' => 'order', * 'relationship' => 'sibling', * 'group' => 'my-elements-weight', * 'subgroup' => 'my-elements-weight-' . $region, * )); * } * @endcode * * In a situation where tree relationships are present, adding multiple * subgroups is not necessary, because the table will contain indentations that * provide enough information about the sibling and parent relationships. See * MenuForm::BuildOverviewForm for an example creating a table * containing parent relationships. * * @param $element * A form element to attach the tableDrag behavior to. * @param array $options * These options are used to generate JavaScript settings necessary to * configure the tableDrag behavior appropriately for this particular table. * An associative array containing the following keys: * - 'table_id': String containing the target table's id attribute. * If the table does not have an id, one will need to be set, * such as . * - 'action': String describing the action to be done on the form item. * Either 'match' 'depth', or 'order': * - 'match' is typically used for parent relationships. * - 'order' is typically used to set weights on other form elements with * the same group. * - 'depth' updates the target element with the current indentation. * - 'relationship': String describing where the "action" option * should be performed. Either 'parent', 'sibling', 'group', or 'self': * - 'parent' will only look for fields up the tree. * - 'sibling' will look for fields in the same group in rows above and * below it. * - 'self' affects the dragged row itself. * - 'group' affects the dragged row, plus any children below it (the entire * dragged group). * - 'group': A class name applied on all related form elements for this action. * - 'subgroup': (optional) If the group has several subgroups within it, this * string should contain the class name identifying fields in the same * subgroup. * - 'source': (optional) If the $action is 'match', this string should contain * the classname identifying what field will be used as the source value * when matching the value in $subgroup. * - 'hidden': (optional) The column containing the field elements may be * entirely hidden from view dynamically when the JavaScript is loaded. Set * to FALSE if the column should not be hidden. * - 'limit': (optional) Limit the maximum amount of parenting in this table. * * @see MenuForm::BuildOverviewForm() */ function drupal_attach_tabledrag(&$element, array $options) { // Add default values to elements. $options = $options + [ 'subgroup' => NULL, 'source' => NULL, 'hidden' => TRUE, 'limit' => 0, ]; $group = $options['group']; $tabledrag_id = &drupal_static(__FUNCTION__); $tabledrag_id = (!isset($tabledrag_id)) ? 0 : $tabledrag_id + 1; // If a subgroup or source isn't set, assume it is the same as the group. $target = $options['subgroup'] ?? $group; $source = $options['source'] ?? $target; $element['#attached']['drupalSettings']['tableDrag'][$options['table_id']][$group][$tabledrag_id] = [ 'target' => $target, 'source' => $source, 'relationship' => $options['relationship'], 'action' => $options['action'], 'hidden' => $options['hidden'], 'limit' => $options['limit'], ]; $element['#attached']['library'][] = 'core/drupal.tabledrag'; } /** * Hides an element from later rendering. * * The first time render() or RenderInterface::render() is called on an element * tree, as each element in the tree is rendered, it is marked with a #printed * flag and the rendered children of the element are cached. Subsequent calls to * render() or RenderInterface::render() will not traverse the child tree of * this element again: they will just use the cached children. So if you want to * hide an element, be sure to call hide() on the element before its parent tree * is rendered for the first time, as it will have no effect on subsequent * renderings of the parent tree. * * @param $element * The element to be hidden. * * @return array * The element. * * @see \Drupal\Core\Render\RendererInterface * @see render() * @see show() */ function hide(&$element) { $element['#printed'] = TRUE; return $element; } /** * Shows a hidden element for later rendering. * * You can also use render($element), which shows the element while rendering * it. * * The first time render() or RenderInterface::render() is called on an element * tree, as each element in the tree is rendered, it is marked with a #printed * flag and the rendered children of the element are cached. Subsequent calls to * render() or RenderInterface::render() will not traverse the child tree of * this element again: they will just use the cached children. So if you want to * show an element, be sure to call show() on the element before its parent tree * is rendered for the first time, as it will have no effect on subsequent * renderings of the parent tree. * * @param $element * The element to be shown. * * @return array * The element. * * @see \Drupal\Core\Render\RendererInterface * @see render() * @see hide() */ function show(&$element) { $element['#printed'] = FALSE; return $element; } /** * Rebuilds the container, flushes all persistent caches, resets all variables, and rebuilds all data structures. * * At times, it is necessary to re-initialize the entire system to account for * changed or new code. This function: * - Rebuilds the container if $kernel is not passed in. * - Clears all persistent caches: * - The bootstrap cache bin containing base system, module system, and theme * system information. * - The common 'default' cache bin containing arbitrary caches. * - The page cache. * - The URL alias path cache. * - Resets all static variables that have been defined via drupal_static(). * - Clears asset (JS/CSS) file caches. * - Updates the system with latest information about extensions (modules and * themes). * - Updates the bootstrap flag for modules implementing bootstrap_hooks(). * - Rebuilds the full database schema information (invoking hook_schema()). * - Rebuilds data structures of all modules (invoking hook_rebuild()). In * core this means * - blocks, node types, date formats and actions are synchronized with the * database * - The 'active' status of fields is refreshed. * - Rebuilds the menu router. * * It's discouraged to call this during a regular page request. * If you call this function in tests, every code afterwards should use the new * container. * * This means the entire system is reset so all caches and static variables are * effectively empty. After that is guaranteed, information about the currently * active code is updated, and rebuild operations are successively called in * order to synchronize the active system according to the current information * defined in code. * * All modules need to ensure that all of their caches are flushed when * hook_cache_flush() is invoked; any previously known information must no * longer exist. All following hook_rebuild() operations must be based on fresh * and current system data. All modules must be able to rely on this contract. * * @see \Drupal\Core\Cache\CacheHelper::getBins() * @see hook_cache_flush() * @see hook_rebuild() * * This function also resets the theme, which means it is not initialized * anymore and all previously added JavaScript and CSS is gone. Normally, this * function is called as an end-of-POST-request operation that is followed by a * redirect, so this effect is not visible. Since the full reset is the whole * point of this function, callers need to take care for backing up all needed * variables and properly restoring or re-initializing them on their own. For * convenience, this function automatically re-initializes the maintenance theme * if it was initialized before. * * @todo Try to clear page/JS/CSS caches last, so cached pages can still be * served during this possibly long-running operation. (Conflict on bootstrap * cache though.) * @todo Add a global lock to ensure that caches are not primed in concurrent * requests. * * @param \Drupal\Core\DrupalKernel|array $kernel * (optional) The Drupal Kernel. It is the caller's responsibility to rebuild * the container if this is passed in. Sometimes drupal_flush_all_caches is * used as a batch operation so $kernel will be an array, in this instance it * will be treated as if it is NULL. */ function drupal_flush_all_caches($kernel = NULL) { // This is executed based on old/previously known information if $kernel is // not passed in, which is sufficient, since new extensions cannot have any // primed caches yet. $module_handler = \Drupal::moduleHandler(); // Flush all persistent caches. $module_handler->invokeAll('cache_flush'); foreach (Cache::getBins() as $cache_backend) { $cache_backend->deleteAll(); } // Flush asset file caches. \Drupal::service('asset.css.collection_optimizer')->deleteAll(); \Drupal::service('asset.js.collection_optimizer')->deleteAll(); \Drupal::service('asset.query_string')->reset(); // Reset all static caches. drupal_static_reset(); // Wipe the Twig PHP Storage cache. \Drupal::service('twig')->invalidate(); // Rebuild profile, profile, theme_engine and theme data. \Drupal::service('extension.list.profile')->reset(); \Drupal::service('extension.list.theme_engine')->reset(); \Drupal::service('theme_handler')->refreshInfo(); // In case the active theme gets requested later in the same request we need // to reset the theme manager. \Drupal::theme()->resetActiveTheme(); if (!$kernel instanceof DrupalKernel) { $kernel = \Drupal::service('kernel'); $kernel->invalidateContainer(); $kernel->rebuildContainer(); } // Rebuild module data that is stored in state. \Drupal::service('extension.list.module')->reset(); // Rebuild all information based on new module data. \Drupal::moduleHandler()->invokeAll('rebuild'); // Clear all plugin caches. \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions(); // Rebuild the menu router based on all rebuilt data. // Important: This rebuild must happen last, so the menu router is guaranteed // to be based on up to date information. \Drupal::service('router.builder')->rebuild(); // Re-initialize the maintenance theme, if the current request attempted to // use it. Unlike regular usages of this function, the installer and update // scripts need to flush all caches during GET requests/page building. if (function_exists('_drupal_maintenance_theme')) { \Drupal::theme()->resetActiveTheme(); drupal_maintenance_theme(); } } /** * Assembles the Drupal Updater registry. * * An Updater is a class that knows how to update various parts of the Drupal * file system, for example to update modules that have newer releases, or to * install a new theme. * * @return array * The Drupal Updater class registry. * * @see \Drupal\Core\Updater\Updater * @see hook_updater_info() * @see hook_updater_info_alter() */ function drupal_get_updaters() { $updaters = &drupal_static(__FUNCTION__); if (!isset($updaters)) { $updaters = \Drupal::moduleHandler()->invokeAll('updater_info'); \Drupal::moduleHandler()->alter('updater_info', $updaters); uasort($updaters, [SortArray::class, 'sortByWeightElement']); } return $updaters; } /** * Assembles the Drupal FileTransfer registry. * * @return array * The Drupal FileTransfer class registry. * * @see \Drupal\Core\FileTransfer\FileTransfer * @see hook_filetransfer_info() * @see hook_filetransfer_info_alter() */ function drupal_get_filetransfer_info() { $info = &drupal_static(__FUNCTION__); if (!isset($info)) { $info = \Drupal::moduleHandler()->invokeAll('filetransfer_info'); \Drupal::moduleHandler()->alter('filetransfer_info', $info); uasort($info, [SortArray::class, 'sortByWeightElement']); } return $info; }