/** * Generates a test feed and simulates last-modified and etags. * * @param $use_last_modified * Set TRUE to send a last modified header. * @param $use_etag * Set TRUE to send an etag. * @param Request $request * Information about the current HTTP request. * * @return \Symfony\Component\HttpFoundation\Response * A feed that forces cache validation. */ public function testFeed($use_last_modified, $use_etag, Request $request) { $response = new Response(); $last_modified = strtotime('Sun, 19 Nov 1978 05:00:00 GMT'); $etag = Crypt::hashBase64($last_modified); $if_modified_since = strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')); $if_none_match = stripslashes($request->server->get('HTTP_IF_NONE_MATCH')); // Send appropriate response. We respond with a 304 not modified on either // etag or on last modified. if ($use_last_modified) { $response->headers->set('Last-Modified', gmdate(DateTimePlus::RFC7231, $last_modified)); } if ($use_etag) { $response->headers->set('ETag', $etag); } // Return 304 not modified if either last modified or etag match. if ($last_modified == $if_modified_since || $etag == $if_none_match) { $response->setStatusCode(304); return $response; } // The following headers force validation of cache. $response->headers->set('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT'); $response->headers->set('Cache-Control', 'must-revalidate'); $response->headers->set('Content-Type', 'application/rss+xml; charset=utf-8'); // Read actual feed from file. $file_name = drupal_get_path('module', 'aggregator_test') . '/aggregator_test_rss091.xml'; $handle = fopen($file_name, 'r'); $feed = fread($handle, filesize($file_name)); fclose($handle); $response->setContent($feed); return $response; }
/** * {@inheritdoc} * * The file name for the CSS or JS cache file is generated from the hash of * the aggregated contents of the files in $data. This forces proxies and * browsers to download new CSS when the CSS changes. */ public function dump($data, $file_extension) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". $filename = $file_extension . '_' . Crypt::hashBase64($data) . '.' . $file_extension; // Create the css/ or js/ path within the files folder. $path = 'public://' . $file_extension; $uri = $path . '/' . $filename; // Create the CSS or JS file. file_prepare_directory($path, FILE_CREATE_DIRECTORY); if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) { return FALSE; } // If CSS/JS gzip compression is enabled and the zlib extension is available // then create a gzipped version of this file. This file is served // conditionally to browsers that accept gzip using .htaccess rules. // It's possible that the rewrite rules in .htaccess aren't working on this // server, but there's no harm (other than the time spent generating the // file) in generating the file anyway. Sites on servers where rewrite rules // aren't working can set css.gzip to FALSE in order to skip // generating a file that won't be used. if (extension_loaded('zlib') && \Drupal::config('system.performance')->get($file_extension . '.gzip')) { if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { return FALSE; } } return $uri; }
/** * Generates a unique hash for identification purposes. * * @param string $source_path * Source path of the redirect. * @param array $source_query * Source query as an array. * @param string $language * Redirect language. * * @return string * Base 64 hash. */ public static function generateHash($source_path, array $source_query, $language) { $hash = array('source' => Unicode::strtolower($source_path), 'language' => $language); if (!empty($source_query)) { $hash['source_query'] = $source_query; } redirect_sort_recursive($hash, 'ksort'); return Crypt::hashBase64(serialize($hash)); }
/** * Tests configuration of advanced actions through administration interface. */ function testActionConfiguration() { // Create a user with permission to view the actions administration pages. $user = $this->drupalCreateUser(array('administer actions')); $this->drupalLogin($user); // Make a POST request to admin/config/system/actions. $edit = array(); $edit['action'] = Crypt::hashBase64('action_goto_action'); $this->drupalPostForm('admin/config/system/actions', $edit, t('Create')); $this->assertResponse(200); // Make a POST request to the individual action configuration page. $edit = array(); $action_label = $this->randomMachineName(); $edit['label'] = $action_label; $edit['id'] = strtolower($action_label); $edit['url'] = 'admin'; $this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('action_goto_action'), $edit, t('Save')); $this->assertResponse(200); // Make sure that the new complex action was saved properly. $this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully saved the complex action."); $this->assertText($action_label, "Make sure the action label appears on the configuration page after we've saved the complex action."); // Make another POST request to the action edit page. $this->clickLink(t('Configure')); preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches); $aid = $matches[1]; $edit = array(); $new_action_label = $this->randomMachineName(); $edit['label'] = $new_action_label; $edit['url'] = 'admin'; $this->drupalPostForm(NULL, $edit, t('Save')); $this->assertResponse(200); // Make sure that the action updated properly. $this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully updated the complex action."); $this->assertNoText($action_label, "Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."); $this->assertText($new_action_label, "Make sure the action label appears on the configuration page after we've updated the complex action."); $this->clickLink(t('Configure')); $element = $this->xpath('//input[@type="text" and @value="admin"]'); $this->assertTrue(!empty($element), 'Make sure the URL appears when re-editing the action.'); // Make sure that deletions work properly. $this->drupalGet('admin/config/system/actions'); $this->clickLink(t('Delete')); $this->assertResponse(200); $edit = array(); $this->drupalPostForm("admin/config/system/actions/configure/{$aid}/delete", $edit, t('Delete')); $this->assertResponse(200); // Make sure that the action was actually deleted. $this->assertRaw(t('The action %action has been deleted.', array('%action' => $new_action_label)), 'Make sure that we get a delete confirmation message.'); $this->drupalGet('admin/config/system/actions'); $this->assertResponse(200); $this->assertNoText($new_action_label, "Make sure the action label does not appear on the overview page after we've deleted the action."); $action = entity_load('action', $aid); $this->assertFalse($action, 'Make sure the action is gone after being deleted.'); }
/** * {@inheritdoc} */ public function process($text, $langcode) { $settings = $this->settings; if ($settings['settings_source'] === 'global') { $config = \Drupal::config('pathologic.settings'); $settings['protocol_style'] = $config->get('protocol_style'); $settings['local_paths'] = $config->get('local_paths'); } else { $settings = $settings['local_settings']; } // @todo Move code from .module file to inside here. return new FilterProcessResult(_pathologic_filter($text, $settings, Crypt::hashBase64(serialize($settings)))); }
/** * Tests configuration of the node_assign_owner_action action. */ public function testAssignOwnerNodeActionConfiguration() { // Create a user with permission to view the actions administration pages. $user = $this->drupalCreateUser(['administer actions']); $this->drupalLogin($user); // Make a POST request to admin/config/system/actions. $edit = []; $edit['action'] = Crypt::hashBase64('node_assign_owner_action'); $this->drupalPostForm('admin/config/system/actions', $edit, t('Create')); $this->assertResponse(200); // Make a POST request to the individual action configuration page. $edit = []; $action_label = $this->randomMachineName(); $edit['label'] = $action_label; $edit['id'] = strtolower($action_label); $edit['owner_uid'] = $user->id(); $this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('node_assign_owner_action'), $edit, t('Save')); $this->assertResponse(200); // Make sure that the new action was saved properly. $this->assertText(t('The action has been successfully saved.'), 'The node_assign_owner_action action has been successfully saved.'); $this->assertText($action_label, 'The label of the node_assign_owner_action action appears on the actions administration page after saving.'); // Make another POST request to the action edit page. $this->clickLink(t('Configure')); preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches); $aid = $matches[1]; $edit = []; $new_action_label = $this->randomMachineName(); $edit['label'] = $new_action_label; $edit['owner_uid'] = $user->id(); $this->drupalPostForm(NULL, $edit, t('Save')); $this->assertResponse(200); // Make sure that the action updated properly. $this->assertText(t('The action has been successfully saved.'), 'The node_assign_owner_action action has been successfully updated.'); $this->assertNoText($action_label, 'The old label for the node_assign_owner_action action does not appear on the actions administration page after updating.'); $this->assertText($new_action_label, 'The new label for the node_assign_owner_action action appears on the actions administration page after updating.'); // Make sure that deletions work properly. $this->drupalGet('admin/config/system/actions'); $this->clickLink(t('Delete')); $this->assertResponse(200); $edit = []; $this->drupalPostForm("admin/config/system/actions/configure/{$aid}/delete", $edit, t('Delete')); $this->assertResponse(200); // Make sure that the action was actually deleted. $this->assertRaw(t('The action %action has been deleted.', ['%action' => $new_action_label]), 'The delete confirmation message appears after deleting the node_assign_owner_action action.'); $this->drupalGet('admin/config/system/actions'); $this->assertResponse(200); $this->assertNoText($new_action_label, 'The label for the node_assign_owner_action action does not appear on the actions administration page after deleting.'); $action = Action::load($aid); $this->assertFalse($action, 'The node_assign_owner_action action is not available after being deleted.'); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $actions = array(); foreach ($this->manager->getDefinitions() as $id => $definition) { if (is_subclass_of($definition['class'], '\\Drupal\\Core\\Plugin\\PluginFormInterface')) { $key = Crypt::hashBase64($id); $actions[$key] = $definition['label'] . '...'; } } $form['parent'] = array('#type' => 'details', '#title' => $this->t('Create an advanced action'), '#attributes' => array('class' => array('container-inline')), '#open' => TRUE); $form['parent']['action'] = array('#type' => 'select', '#title' => $this->t('Action'), '#title_display' => 'invisible', '#options' => $actions, '#empty_option' => $this->t('Choose an advanced action')); $form['parent']['actions'] = array('#type' => 'actions'); $form['parent']['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Create')); return $form; }
/** * {@inheritdoc} * * @param string $action_id * The hashed version of the action ID. */ public function buildForm(array $form, FormStateInterface $form_state, $action_id = NULL) { // In \Drupal\action\Form\ActionAdminManageForm::buildForm() the action // are hashed. Here we have to decrypt it to find the desired action ID. foreach ($this->actionManager->getDefinitions() as $id => $definition) { $key = Crypt::hashBase64($id); if ($key === $action_id) { $this->entity->setPlugin($id); // Derive the label and type from the action definition. $this->entity->set('label', $definition['label']); $this->entity->set('type', $definition['type']); break; } } return parent::buildForm($form, $form_state); }
public function testActionEntityClone() { foreach (\Drupal::service('plugin.manager.action')->getDefinitions() as $id => $definition) { if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface') && $definition['label'] == 'Send email') { $action_key = Crypt::hashBase64($id); break; } } $edit = [ 'label' => 'Test send email action for clone', 'id' => 'test_send_email_for_clone', 'recipient' => '*****@*****.**', 'subject' => 'test subject', 'message' => 'test message', ]; $this->drupalPostForm("admin/config/system/actions/add/$action_key", $edit, t('Save')); $actions = \Drupal::entityTypeManager() ->getStorage('action') ->loadByProperties([ 'id' => $edit['id'], ]); $action = reset($actions); $edit = [ 'label' => 'Test send email action cloned', 'id' => 'test_send_email_cloned', ]; $this->drupalPostForm('entity_clone/action/' . $action->id(), $edit, t('Clone')); $actions = \Drupal::entityTypeManager() ->getStorage('action') ->loadByProperties([ 'id' => $edit['id'], ]); $action = reset($actions); $this->assertTrue($action, 'Test action cloned found in database.'); }
/** * Reinstall changed config files. */ public function autoImportConfig() { $config = $this->getSettings(); $changed = FALSE; foreach ($config->get('auto_import') as $key => $file) { $contents = @file_get_contents($file['filename']); if (!$contents) { continue; } $hash = Crypt::hashBase64($contents); if ($hash != $file['hash']) { $changed = TRUE; $config->set("auto_import.{$key}.hash", $hash); $data = (new InstallStorage())->decode($contents); $config_name = basename($file['filename'], '.yml'); $entity_type_id = $this->configManager->getEntityTypeIdByName($config_name); if ($entity_type_id) { $entity_storage = $this->getStorage($entity_type_id); $entity_id = $this->getEntityId($entity_storage, $config_name); $entity_type = $entity_storage->getEntityType(); $id_key = $entity_type->getKey('id'); $data[$id_key] = $entity_id; $entity = $entity_storage->create($data); if ($existing_entity = $entity_storage->load($entity_id)) { $entity->set('uuid', $existing_entity->uuid())->enforceIsNew(FALSE); } $entity_storage->save($entity); } else { $this->configFactory->getEditable($config_name)->setData($data)->save(); } } } if ($changed) { $config->save(); } }
/** * Get a typed data instance for a property of a given typed data object. * * This method will use prototyping for fast and efficient instantiation of * many property objects with the same property path; e.g., * when multiple comments are used comment_body.0.value needs to be * instantiated very often. * Prototyping is done by the root object's data type and the given * property path, i.e. all property instances having the same property path * and inheriting from the same data type are prototyped. * * @param \Drupal\Core\TypedData\TypedDataInterface $object * The parent typed data object, implementing the TypedDataInterface and * either the ListInterface or the ComplexDataInterface. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. * @param mixed $value * (optional) The data value. If set, it has to match one of the supported * data type formats as documented by the data type classes. * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not * implement the ListInterface or the ComplexDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() */ public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { $definition = $object->getRoot()->getDataDefinition(); // If the definition is a list, we need to look at the data type and the // settings of its item definition. if ($definition instanceof ListDataDefinition) { $definition = $definition->getItemDefinition(); } $key = $definition->getDataType(); if ($settings = $definition->getSettings()) { $key .= ':' . Crypt::hashBase64(serialize($settings)); } $key .= ':' . $object->getPropertyPath() . '.'; // If we are creating list items, we always use 0 in the key as all list // items look the same. $key .= is_numeric($property_name) ? 0 : $property_name; // Make sure we have a prototype. Then, clone the prototype and set object // specific values, i.e. the value and the context. if (!isset($this->prototypes[$key]) || !$key) { // Create the initial prototype. For that we need to fetch the definition // of the to be created property instance from the parent. if ($object instanceof ComplexDataInterface) { $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); } elseif ($object instanceof ListInterface) { $definition = $object->getItemDefinition(); } else { throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface."); } // Make sure we have got a valid definition. if (!$definition) { throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.'); } // Now create the prototype using the definition, but do not pass the // given value as it will serve as prototype for any further instance. $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object); } // Clone from the prototype, then update the parent relationship and set the // data value if necessary. $property = clone $this->prototypes[$key]; $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value, FALSE); } return $property; }
/** * {@inheritdoc} */ public function getContext() { $sid = $this->requestStack->getCurrentRequest()->getSession()->getId(); return Crypt::hashBase64($sid); }
/** * Calculates the aggregated file URI of a group of JavaScript assets. * * @param array $js_assets * A group of JavaScript assets. * @return string * A file URI. * * @see testAggregation() * @see testAggregationOrder() */ protected function calculateAggregateFilename($js_assets) { $data = ''; foreach ($js_assets as $js_asset) { $data .= file_get_contents($js_asset['data']) . ";\n"; } return file_create_url('public://js/js_' . Crypt::hashBase64($data) . '.js'); }
/** * Store the Session ID and ticket for single-log-out purposes. * * @param string $session_id * The session ID, to be used to kill the session later. * @param string $ticket * The CAS service ticket to be used as the lookup key. * * @codeCoverageIgnore */ protected function storeLoginSessionData($session_id, $ticket) { if ($this->settings->get('cas.settings')->get('logout.enable_single_logout') === TRUE) { $plainsid = $session_id; } else { $plainsid = ''; } $this->connection->insert('cas_login_data')->fields(array('sid', 'plainsid', 'ticket'), array(Crypt::hashBase64($session_id), $plainsid, $ticket))->execute(); }
/** * Normalizes a cache ID in order to comply with database limitations. * * @param string $cid * The passed in cache ID. * * @return string * An ASCII-encoded cache ID that is at most 255 characters long. */ protected function normalizeCid($cid) { // Nothing to do if the ID is a US ASCII string of 255 characters or less. $cid_is_ascii = mb_check_encoding($cid, 'ASCII'); if (strlen($cid) <= 255 && $cid_is_ascii) { return $cid; } // Return a string that uses as much as possible of the original cache ID // with the hash appended. $hash = Crypt::hashBase64($cid); if (!$cid_is_ascii) { return $hash; } return substr($cid, 0, 255 - strlen($hash)) . $hash; }
/** * Store the Session ID and ticket for single-log-out purposes. * * @param string $session_id * The hashed session ID, to be used to kill the session later. * @param string $ticket * The CAS service ticket to be used as the lookup key. * * @codeCoverageIgnore */ protected function storeLoginSessionData($session_id, $ticket) { $this->connection->insert('cas_login_data')->fields(array('sid', 'ticket'), array(Crypt::hashBase64($session_id), $ticket))->execute(); }
/** * Returns whether a given user account is logged in. * * @param \Drupal\user\UserInterface $account * The user account object to check. * * @return bool * Return TRUE if the user is logged in, FALSE otherwise. */ protected function drupalUserIsLoggedIn(UserInterface $account) { if (!isset($account->sessionId)) { return FALSE; } // The session ID is hashed before being stored in the database. // @see \Drupal\Core\Session\SessionHandler::read() return (bool) db_query("SELECT sid FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid AND u.default_langcode = 1 WHERE s.sid = :sid", array(':sid' => Crypt::hashBase64($account->sessionId)))->fetchField(); }
/** * Migrates the current session to a new session id. * * @param string $old_session_id * The old session id. The new session id is $this->getId() unless * $new_insecure_session_id is not empty. */ protected function migrateStoredSession($old_session_id) { $fields = array('sid' => Crypt::hashBase64($this->getId())); $this->connection->update('sessions')->fields($fields)->condition('sid', Crypt::hashBase64($old_session_id))->execute(); }
/** * Test that there exists a session with two specific session IDs. * * @param $sid * The insecure session ID to search for. * @param $assertion_text * The text to display when we perform the assertion. * * @return * The result of assertTrue() that there's a session in the system that * has the given insecure and secure session IDs. */ protected function assertSessionIds($sid, $assertion_text) { $args = array(':sid' => Crypt::hashBase64($sid)); return $this->assertTrue(db_query('SELECT timestamp FROM {sessions} WHERE sid = :sid', $args)->fetchField(), $assertion_text); }
/** * Builds the table portion of the form for the book administration page. * * @param \Drupal\node\NodeInterface $node * The node of the top-level page in the book. * @param array $form * The form that is being modified, passed by reference. * * @see self::buildForm() */ protected function bookAdminTable(NodeInterface $node, array &$form) { $form['table'] = array('#theme' => 'book_admin_table', '#tree' => TRUE); $tree = $this->bookManager->bookSubtreeData($node->book); // Do not include the book item itself. $tree = array_shift($tree); if ($tree['below']) { $hash = Crypt::hashBase64(serialize($tree['below'])); // Store the hash value as a hidden form element so that we can detect // if another user changed the book hierarchy. $form['tree_hash'] = array('#type' => 'hidden', '#default_value' => $hash); $form['tree_current_hash'] = array('#type' => 'value', '#value' => $hash); $this->bookAdminTableTree($tree['below'], $form['table']); } }
/** * Tests data type handling. */ public function testDataTypes() { \Drupal::service('module_installer')->install(array('config_test')); $storage = new DatabaseStorage($this->container->get('database'), 'config'); $name = 'config_test.types'; $config = $this->config($name); $original_content = file_get_contents(drupal_get_path('module', 'config_test') . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY . "/{$name}.yml"); $this->verbose('<pre>' . $original_content . "\n" . var_export($storage->read($name), TRUE)); // Verify variable data types are intact. $data = array('array' => array(), 'boolean' => TRUE, 'exp' => 1.2E+34, 'float' => 3.14159, 'float_as_integer' => (double) 1, 'hex' => 0xc, 'int' => 99, 'octal' => 0775, 'string' => 'string', 'string_int' => '1'); $data['_core']['default_config_hash'] = Crypt::hashBase64(serialize($data)); $this->assertIdentical($config->get(), $data); // Re-set each key using Config::set(). foreach ($data as $key => $value) { $config->set($key, $value); } $config->save(); $this->assertIdentical($config->get(), $data); // Assert the data against the file storage. $this->assertIdentical($storage->read($name), $data); $this->verbose('<pre>' . $name . var_export($storage->read($name), TRUE)); // Set data using config::setData(). $config->setData($data)->save(); $this->assertIdentical($config->get(), $data); $this->assertIdentical($storage->read($name), $data); // Test that schema type enforcement can be overridden by trusting the data. $this->assertIdentical(99, $config->get('int')); $config->set('int', '99')->save(TRUE); $this->assertIdentical('99', $config->get('int')); // Test that re-saving without testing the data enforces the schema type. $config->save(); $this->assertIdentical($data, $config->get()); // Test that setting an unsupported type for a config object with a schema // fails. try { $config->set('stream', fopen(__FILE__, 'r'))->save(); $this->fail('No Exception thrown upon saving invalid data type.'); } catch (UnsupportedDataTypeConfigException $e) { $this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array('%class' => get_class($e)))); } // Test that setting an unsupported type for a config object with no schema // also fails. $typed_config_manager = $this->container->get('config.typed'); $config_name = 'config_test.no_schema'; $config = $this->config($config_name); $this->assertFalse($typed_config_manager->hasConfigSchema($config_name)); try { $config->set('stream', fopen(__FILE__, 'r'))->save(); $this->fail('No Exception thrown upon saving invalid data type.'); } catch (UnsupportedDataTypeConfigException $e) { $this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array('%class' => get_class($e)))); } }
/** * {@inheritdoc} */ public function destroy($sid) { // Delete session data. $this->connection->delete('sessions')->condition('sid', Crypt::hashBase64($sid))->execute(); // Reset $_SESSION and current user to prevent a new session from being // started in \Drupal\Core\Session\SessionManager::save(). $_SESSION = array(); \Drupal::currentUser()->setAccount(new AnonymousUserSession()); // Unset the session cookies. $this->deleteCookie($this->getName()); return TRUE; }
/** * {@inheritdoc} */ public function destroy($sid) { // Delete session data. $this->connection->delete('sessions')->condition('sid', Crypt::hashBase64($sid))->execute(); return TRUE; }
/** * Builds the table portion of the form for the book administration page. * * @param \Drupal\node\NodeInterface $node * The node of the top-level page in the book. * @param array $form * The form that is being modified, passed by reference. * * @see self::buildForm() */ protected function bookAdminTable(NodeInterface $node, array &$form) { $form['table'] = array('#type' => 'table', '#header' => [$this->t('Title'), $this->t('Weight'), $this->t('Parent'), $this->t('Operations')], '#empty' => $this->t('No book content available.'), '#tabledrag' => [['action' => 'match', 'relationship' => 'parent', 'group' => 'book-pid', 'subgroup' => 'book-pid', 'source' => 'book-nid', 'hidden' => TRUE, 'limit' => BookManager::BOOK_MAX_DEPTH - 2], ['action' => 'order', 'relationship' => 'sibling', 'group' => 'book-weight']]); $tree = $this->bookManager->bookSubtreeData($node->book); // Do not include the book item itself. $tree = array_shift($tree); if ($tree['below']) { $hash = Crypt::hashBase64(serialize($tree['below'])); // Store the hash value as a hidden form element so that we can detect // if another user changed the book hierarchy. $form['tree_hash'] = array('#type' => 'hidden', '#default_value' => $hash); $form['tree_current_hash'] = array('#type' => 'value', '#value' => $hash); $this->bookAdminTableTree($tree['below'], $form['table']); } }
/** * {@inheritdoc} */ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { $theme_info = $this->themeManager->getActiveTheme(); // Add the theme name to the cache key since themes may implement // hook_library_info_alter(). Additionally add the current language to // support translation of JavaScript files via hook_js_alter(). $libraries_to_load = $this->getLibrariesToLoad($assets); $cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager->getCurrentLanguage()->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize; if ($cached = $this->cache->get($cid)) { list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data; } else { $javascript = []; $default_options = ['type' => 'file', 'group' => JS_DEFAULT, 'weight' => 0, 'cache' => TRUE, 'preprocess' => TRUE, 'attributes' => [], 'version' => NULL, 'browsers' => []]; // Collect all libraries that contain JS assets and are in the header. $header_js_libraries = []; foreach ($libraries_to_load as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['js']) && !empty($definition['header'])) { $header_js_libraries[] = $library; } } // The current list of header JS libraries are only those libraries that // are in the header, but their dependencies must also be loaded for them // to function correctly, so update the list with those. $header_js_libraries = $this->libraryDependencyResolver->getLibrariesWithDependencies($header_js_libraries); foreach ($libraries_to_load as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['js'])) { foreach ($definition['js'] as $options) { $options += $default_options; // 'scope' is a calculated option, based on which libraries are // marked to be loaded from the header (see above). $options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer'; // Preprocess can only be set if caching is enabled and no // attributes are set. $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE; // Always add a tiny value to the weight, to conserve the insertion // order. $options['weight'] += count($javascript) / 1000; // Local and external files must keep their name as the associative // key so the same JavaScript file is not added twice. $javascript[$options['data']] = $options; } } } // Allow modules and themes to alter the JavaScript assets. $this->moduleHandler->alter('js', $javascript, $assets); $this->themeManager->alter('js', $javascript, $assets); // Sort JavaScript assets, so that they appear in the correct order. uasort($javascript, 'static::sort'); // Prepare the return value: filter JavaScript assets per scope. $js_assets_header = []; $js_assets_footer = []; foreach ($javascript as $key => $item) { if ($item['scope'] == 'header') { $js_assets_header[$key] = $item; } elseif ($item['scope'] == 'footer') { $js_assets_footer[$key] = $item; } } if ($optimize) { $collection_optimizer = \Drupal::service('asset.js.collection_optimizer'); $js_assets_header = $collection_optimizer->optimize($js_assets_header); $js_assets_footer = $collection_optimizer->optimize($js_assets_footer); } // If the core/drupalSettings library is being loaded or is already // loaded, get the JavaScript settings assets, and convert them into a // single "regular" JavaScript asset. $libraries_to_load = $this->getLibrariesToLoad($assets); $settings_required = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries())); $settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0; // Initialize settings to FALSE since they are not needed by default. This // distinguishes between an empty array which must still allow // hook_js_settings_alter() to be run. $settings = FALSE; if ($settings_required && $settings_have_changed) { $settings = $this->getJsSettingsAssets($assets); // Allow modules to add cached JavaScript settings. foreach ($this->moduleHandler->getImplementations('js_settings_build') as $module) { $function = $module . '_' . 'js_settings_build'; $function($settings, $assets); } } $settings_in_header = in_array('core/drupalSettings', $header_js_libraries); $this->cache->set($cid, [$js_assets_header, $js_assets_footer, $settings, $settings_in_header], CacheBackendInterface::CACHE_PERMANENT, ['library_info']); } if ($settings !== FALSE) { // Attached settings override both library definitions and // hook_js_settings_build(). $settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE); // Allow modules and themes to alter the JavaScript settings. $this->moduleHandler->alter('js_settings', $settings, $assets); $this->themeManager->alter('js_settings', $settings, $assets); // Update the $assets object accordingly, so that it reflects the final // settings. $assets->setSettings($settings); $settings_as_inline_javascript = ['type' => 'setting', 'group' => JS_SETTING, 'weight' => 0, 'browsers' => [], 'data' => $settings]; $settings_js_asset = ['drupalSettings' => $settings_as_inline_javascript]; // Prepend to the list of JS assets, to render it first. Preferably in // the footer, but in the header if necessary. if ($settings_in_header) { $js_assets_header = $settings_js_asset + $js_assets_header; } else { $js_assets_footer = $settings_js_asset + $js_assets_footer; } } return [$js_assets_header, $js_assets_footer]; }
/** * Tests \Drupal\Component\Utility\Crypt::hashBase64(). * * @param string $data * Data to hash. * @param string $expected_hash * Expected result from hashing $data. * * @dataProvider providerTestHashBase64 */ public function testHashBase64($data, $expected_hash) { $hash = Crypt::hashBase64($data); $this->assertEquals($expected_hash, $hash, 'The correct hash was not calculated.'); }
/** * Generates a prefix for APCu user cache keys. * * A standardized prefix is useful to allow visual inspection of an APCu user * cache. By default, this method will produce a unique prefix per site using * the hash salt. If the setting 'apcu_ensure_unique_prefix' is set to FALSE * then if the caller does not provide a $site_path only the Drupal root will * be used. This allows WebTestBase to use the same prefix ensuring that the * number of APCu items created during a full test run is kept to a minimum. * Additionally, if a multi site implementation does not use site specific * module directories setting apcu_ensure_unique_prefix would allow the sites * to share APCu cache items. * * @param $identifier * An identifier for the prefix. For example, 'class_loader' or * 'cache_backend'. * * @return string * The prefix for APCu user cache keys. */ public static function getApcuPrefix($identifier, $root, $site_path = '') { if (static::get('apcu_ensure_unique_prefix', TRUE)) { return 'drupal.' . $identifier . '.' . \Drupal::VERSION . '.' . static::get('deployment_identifier') . '.' . hash_hmac('sha256', $identifier, static::get('hash_salt') . '.' . $root . '/' . $site_path); } return 'drupal.' . $identifier . '.' . \Drupal::VERSION . '.' . static::get('deployment_identifier') . '.' . Crypt::hashBase64($root . '/' . $site_path); }
/** * Remove sessions marked for garbage collection. */ protected function cleanupObsoleteSessions() { foreach ($this->obsoleteSessionIds as $sid => $key) { $this->connection->delete('sessions')->condition($key, Crypt::hashBase64($sid))->execute(); } }
/** * Ensures that cache IDs have a maximum length of 255 characters. * * @param string $cid * The passed in cache ID. * * @return string * A cache ID that is at most 255 characters long. */ protected function normalizeCid($cid) { // Nothing to do if the ID length is 255 characters or less. if (strlen($cid) <= 255) { return $cid; } // Return a string that uses as much as possible of the original cache ID // with the hash appended. $hash = Crypt::hashBase64($cid); return substr($cid, 0, 255 - strlen($hash)) . $hash; }
/** * Constructs an ApcuBackendFactory object. * * @param string $root * The app root. * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider * The cache tags checksum provider. */ public function __construct($root, CacheTagsChecksumInterface $checksum_provider) { $this->sitePrefix = Crypt::hashBase64($root . '/' . conf_path()); $this->checksumProvider = $checksum_provider; }