From d1ed52892837d22c16e8b77d4883a3ad9ba774be Mon Sep 17 00:00:00 2001 From: Dave Reid Date: Tue, 24 Jun 2014 19:41:04 -0500 Subject: [PATCH 001/169] Issue #2272871 by Elijah Lynn, Dave Reid: Fixed "Do nothing. Leave old alias intact" does not uncheck "Generate automatic URL alias" checkbox on first edit of existing content, but does on subsequent edits. --- pathauto.inc | 2 +- pathauto.module | 10 +++++++--- pathauto.test | 13 +++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pathauto.inc b/pathauto.inc index 04b0e96..d29ca10 100644 --- a/pathauto.inc +++ b/pathauto.inc @@ -369,7 +369,7 @@ function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $lang // Special handling when updating an item which is already aliased. $existing_alias = NULL; - if ($op == 'update' || $op == 'bulkupdate') { + if ($op != 'insert') { if ($existing_alias = _pathauto_existing_alias_data($source, $language)) { switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) { case PATHAUTO_UPDATE_ACTION_NO_NEW: diff --git a/pathauto.module b/pathauto.module index 7c83068..e0fac9f 100644 --- a/pathauto.module +++ b/pathauto.module @@ -302,9 +302,13 @@ function pathauto_field_attach_form($entity_type, $entity, &$form, &$form_state, if (!empty($id)) { module_load_include('inc', 'pathauto'); $uri = entity_uri($entity_type, $entity); - $path = drupal_get_path_alias($uri['path'], $langcode); - $pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode); - $entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias); + if ($pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode)) { + $path = drupal_get_path_alias($uri['path'], $langcode); + $entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias); + } + else { + $entity->path['pathauto'] = FALSE; + } } else { $entity->path['pathauto'] = TRUE; diff --git a/pathauto.test b/pathauto.test index 4bfe2b3..04649b1 100644 --- a/pathauto.test +++ b/pathauto.test @@ -261,7 +261,7 @@ class PathautoUnitTestCase extends PathautoTestHelper { $node->title = 'Fifth title'; pathauto_node_update($node); $this->assertEntityAlias('node', $node, 'content/fourth-title'); - $this->assertNoAliasExists(array('alias' => 'content/fith-title')); + $this->assertNoAliasExists(array('alias' => 'content/fifth-title')); // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'update'. $this->deleteAllAliases(); @@ -425,13 +425,22 @@ class PathautoFunctionalTestCase extends PathautoFunctionalTestHelper { $this->drupalGet($automatic_alias); $this->assertText($title, 'Node accessible through automatic alias.'); + // Disable the update action. + variable_set('pathauto_update_action', 0); + $this->drupalGet("node/{$node->nid}/edit"); + $this->assertNoFieldChecked('edit-path-pathauto'); + + variable_del('pathauto_update_action'); + $this->drupalGet("node/{$node->nid}/edit"); + $this->assertFieldChecked('edit-path-pathauto'); + // Manually set the node's alias. $manual_alias = 'content/' . $node->nid; $edit = array( 'path[pathauto]' => FALSE, 'path[alias]' => $manual_alias, ); - $this->drupalPost("node/{$node->nid}/edit", $edit, t('Save')); + $this->drupalPost(NULL, $edit, t('Save')); $this->assertText("Basic page $title has been updated."); // Check that the automatic alias checkbox is now unchecked by default. From f98f632202afb3e229097e2d4f7a42062a8899fb Mon Sep 17 00:00:00 2001 From: Dave Reid Date: Wed, 25 Jun 2014 01:01:06 -0500 Subject: [PATCH 002/169] Issue #2272871 by Dave Reid, Elijah Lynn: Fixed "Do nothing. Leave old alias intact" update action should hide the Pathauto checkbox on existing content. --- pathauto.inc | 20 +++++++++++--------- pathauto.module | 30 ++++++++++++++++++------------ pathauto.test | 25 +++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/pathauto.inc b/pathauto.inc index d29ca10..63b9244 100644 --- a/pathauto.inc +++ b/pathauto.inc @@ -341,8 +341,10 @@ function pathauto_clean_alias($alias) { * (e.g., $node->type). * @param $language * A string specify the path's language. - * @return - * The alias that was created. + * + * @return array|null|false + * The alias array that was created, NULL if an empty alias was generated, or + * FALSE if the alias generation was not possible. * * @see _pathauto_set_alias() * @see token_replace() @@ -364,7 +366,7 @@ function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $lang if (empty($pattern)) { // No pattern? Do nothing (otherwise we may blow away existing aliases...) - return ''; + return FALSE; } // Special handling when updating an item which is already aliased. @@ -375,7 +377,7 @@ function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $lang case PATHAUTO_UPDATE_ACTION_NO_NEW: // If an alias already exists, and the update action is set to do nothing, // then gosh-darn it, do nothing. - return ''; + return FALSE; } } } @@ -394,7 +396,7 @@ function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $lang // @see token_scan() $pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern); if ($alias === $pattern_tokens_removed) { - return ''; + return; } $alias = pathauto_clean_alias($alias); @@ -406,7 +408,7 @@ function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $lang // If we have arrived at an empty string, discontinue. if (!drupal_strlen($alias)) { - return ''; + return; } // If the alias already exists, generate a new, hopefully unique, variant. @@ -516,7 +518,7 @@ function _pathauto_path_is_callback($path) { * An optional string with the operation being performed. * * @return - * The saved path from path_save() or NULL if the path was not saved. + * The saved path from path_save() or FALSE if the path was not saved. * * @see path_save() */ @@ -528,7 +530,7 @@ function _pathauto_set_alias(array $path, $existing_alias = NULL, $op = NULL) { if ($verbose) { _pathauto_verbose(t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias']))); } - return; + return FALSE; } // Skip replacing the current alias with an identical alias @@ -540,7 +542,7 @@ function _pathauto_set_alias(array $path, $existing_alias = NULL, $op = NULL) { switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) { case PATHAUTO_UPDATE_ACTION_NO_NEW: // Do not create the alias. - return; + return FALSE; case PATHAUTO_UPDATE_ACTION_LEAVE: // Create a new alias instead of overwriting the existing by leaving // $path['pid'] empty. diff --git a/pathauto.module b/pathauto.module index e0fac9f..9bb132d 100644 --- a/pathauto.module +++ b/pathauto.module @@ -302,12 +302,18 @@ function pathauto_field_attach_form($entity_type, $entity, &$form, &$form_state, if (!empty($id)) { module_load_include('inc', 'pathauto'); $uri = entity_uri($entity_type, $entity); - if ($pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode)) { - $path = drupal_get_path_alias($uri['path'], $langcode); - $entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias); + $pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode); + if ($pathauto_alias === FALSE) { + // If Pathauto is not going to be able to generate an alias, then we + // should not bother to show the checkbox since it wouldn't do anything. + // Note that if a pattern does apply, but all the tokens currently + // evaluate to empty strings, then $pathauto_alias would equal null and + // not false. + return; } else { - $entity->path['pathauto'] = FALSE; + $path = drupal_get_path_alias($uri['path'], $langcode); + $entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias); } } else { @@ -565,14 +571,14 @@ function pathauto_node_operations() { function pathauto_node_update_alias(stdClass $node, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the node. if (isset($node->path['pathauto']) && empty($node->path['pathauto']) && empty($options['force'])) { - return; + return FALSE; } $options += array('language' => pathauto_entity_language('node', $node)); // Skip processing if the node has no pattern. if (!pathauto_pattern_load_by_entity('node', $node->type, $options['language'])) { - return; + return FALSE; } module_load_include('inc', 'pathauto'); @@ -665,7 +671,7 @@ function pathauto_form_taxonomy_form_term_alter(&$form, $form_state) { function pathauto_taxonomy_term_update_alias(stdClass $term, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the term. if (isset($term->path['pathauto']) && empty($term->path['pathauto']) && empty($options['force'])) { - return; + return FALSE; } $module = 'taxonomy_term'; @@ -674,7 +680,7 @@ function pathauto_taxonomy_term_update_alias(stdClass $term, $op, array $options $module = 'forum'; } else { - return; + return FALSE; } } @@ -691,7 +697,7 @@ function pathauto_taxonomy_term_update_alias(stdClass $term, $op, array $options // Skip processing if the term has no pattern. if (!pathauto_pattern_load_by_entity($module, $term->vocabulary_machine_name)) { - return; + return FALSE; } module_load_include('inc', 'pathauto'); @@ -798,7 +804,7 @@ function pathauto_user_operations() { function pathauto_user_update_alias(stdClass $account, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the account. if (isset($account->path['pathauto']) && empty($account->path['pathauto']) && empty($options['force'])) { - return; + return FALSE; } $options += array( @@ -810,7 +816,7 @@ function pathauto_user_update_alias(stdClass $account, $op, array $options = arr // Skip processing if the account has no pattern. if (!pathauto_pattern_load_by_entity('user', '', $options['language'])) { - return; + return FALSE; } module_load_include('inc', 'pathauto'); @@ -870,7 +876,7 @@ function pathauto_user_update_action($account, $context = array()) { function pathauto_blog_update_alias(stdClass $account, $op, array $options = array()) { // Skip processing if the blog has no pattern. if (!pathauto_pattern_load_by_entity('blog')) { - return; + return FALSE; } $options += array( diff --git a/pathauto.test b/pathauto.test index 04649b1..5a408e0 100644 --- a/pathauto.test +++ b/pathauto.test @@ -425,11 +425,12 @@ class PathautoFunctionalTestCase extends PathautoFunctionalTestHelper { $this->drupalGet($automatic_alias); $this->assertText($title, 'Node accessible through automatic alias.'); - // Disable the update action. + // Disable the update action. The checkbox should not be visible. variable_set('pathauto_update_action', 0); $this->drupalGet("node/{$node->nid}/edit"); - $this->assertNoFieldChecked('edit-path-pathauto'); + $this->assertNoFieldById('edit-path-pathauto'); + // Reset the update action back to default. The checkbox should be visible. variable_del('pathauto_update_action'); $this->drupalGet("node/{$node->nid}/edit"); $this->assertFieldChecked('edit-path-pathauto'); @@ -475,6 +476,26 @@ class PathautoFunctionalTestCase extends PathautoFunctionalTestHelper { $this->assertNoFieldById('edit-path-pathauto'); $this->assertFieldByName('path[alias]', ''); $this->assertNoEntityAlias('node', $node); + + // Set the page pattern to use only tokens so we can test the checkbox + // behavior if none of the tokens have a value currently. + variable_set('pathauto_node_page_pattern', '[node:title]'); + + // Create a node with an empty title. The Pathauto checkbox should still be + // visible but unchecked. + $node = $this->drupalCreateNode(array('type' => 'page', 'title' => '')); + $this->drupalGet('node/' . $node->nid . '/edit'); + $this->assertNoFieldChecked('edit-path-pathauto'); + $this->assertFieldByName('path[alias]', ''); + $this->assertNoEntityAlias('node', $node); + + $edit = array(); + $edit['title'] = 'Valid title'; + $edit['path[pathauto]'] = TRUE; + $this->drupalPost(NULL, $edit, t('Save')); + $this->drupalGet('node/' . $node->nid . '/edit'); + $this->assertFieldChecked('edit-path-pathauto'); + $this->assertFieldByName('path[alias]', 'valid-title'); } /** From e7562ad81db4b5c82e7e23eef6e9f66158f87dce Mon Sep 17 00:00:00 2001 From: Dave Reid Date: Tue, 7 Oct 2014 09:42:57 -0500 Subject: [PATCH 003/169] Fixed error in hook_pathauto_pattern_alter() example code. --- pathauto.api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.api.php b/pathauto.api.php index d7e2346..37239cb 100644 --- a/pathauto.api.php +++ b/pathauto.api.php @@ -58,7 +58,7 @@ function hook_pathauto_is_alias_reserved($alias, $source, $langcode) { */ function hook_pathauto_pattern_alter(&$pattern, array &$context) { // Switch out any [node:created:*] tokens with [node:updated:*] on update. - if ($module == 'node' && ($context['op'] == 'update')) { + if ($context['module'] == 'node' && ($context['op'] == 'update')) { $pattern = preg_replace('/\[node:created(\:[^]]*)?\]/', '[node:updated$1]', $pattern); } } From ca46f1f6415148b67292b3b75ea10ef5465fcf7d Mon Sep 17 00:00:00 2001 From: davereid Date: Sun, 1 Feb 2015 15:33:15 -0600 Subject: [PATCH 004/169] Issue #801702 by IceCreamYou, Dave Reid, m4olivei, mojzis: Updated API documentation. --- API.txt | 149 ----------------------------------------------- README.txt | 30 ++++------ pathauto.api.php | 112 +++++++++++++++++++++++++++++++++-- 3 files changed, 117 insertions(+), 174 deletions(-) delete mode 100644 API.txt diff --git a/API.txt b/API.txt deleted file mode 100644 index e585cd4..0000000 --- a/API.txt +++ /dev/null @@ -1,149 +0,0 @@ -This document explains how to provide "Pathauto integration" in a -module. You need this if you would like to provide additional tokens -or if your module has paths and you wish to have them automatically -aliased. The simplest integration is just to provide tokens so we -cover that first. More advanced integration requires an -implementation of hook_pathauto to provide a settings form. - -It may be helpful to review some examples of integration from the -pathauto_node.inc, pathauto_taxonomy.inc, and pathauto_user.inc files. - - -================== -1 - Providing additional tokens -================== - -If all you want is to enable tokens for your module you will simply -need to implement two functions: - - hook_token_values - hook_token_list - -See the token.module and it's API.txt for more information about this -process. - -If the token is intended to generate a path expected to contain slashes, -the token name must end in 'path', 'path-raw' or 'alias'. This indicates to -Pathauto that the slashes should not be removed from the replacement value. - -When an object is created (whether it is a node or a user or a -taxonomy term) the data that Pathauto hands to the token_values in the -$object is in a specific format. This is the format that most people -write code to handle. However, during edits and bulk updates the data -may be in a totally different format. So, if you are writing a -hook_token_values implementation to add special tokens, be sure to -test creation, edit, and bulk update cases to make sure your code will -handle it. - -================== -2 - Settings hook - To create aliases for your module -================== -You must implement hook_pathauto($op), where $op is always (at this -time) 'settings'. Return an object (NOT an array) containing the -following members, which will be used by pathauto to build a group -of settings for your module and define the variables for saving your -settings: - -module - The name of your module (e.g., 'node') -groupheader - The translated label for the settings group (e.g., - t('Content path settings') -patterndescr - The translated label for the default pattern (e.g., - t('Default path pattern (applies to all content types with blank patterns below)') -patterndefault - A translated default pattern (e.g., t('[cat]/[title].html')) -token_type - The token type (e.g. 'node', 'user') that can be used. -patternitems - For modules which need to express multiple patterns - (for example, the node module supports a separate pattern for each - content type), an array whose keys consist of identifiers for each - pattern (e.g., the content type name) and values consist of the - translated label for the pattern -bulkname - For modules which support a bulk update operation, the - translated label for the action (e.g., t('Bulk update content paths')) -bulkdescr - For modules which support a bulk update operation, a - translated, more thorough description of what the operation will do - (e.g., t('Generate aliases for all existing content items which do not already have aliases.')) - - -================== -2 - $alias = pathauto_create_alias($module, $op, $placeholders, $src, $type=NULL) -================== - -At the appropriate time (usually when a new item is being created for -which a generated alias is desired), call pathauto_create_alias() to -generate and create the alias. See the user, taxonomy, and nodeapi hook -implementations in pathauto.module for examples. - -$module - The name of your module (e.g., 'node') -$op - Operation being performed on the item ('insert', 'update', or - 'bulkupdate') -$placeholders - An array whose keys consist of the translated placeholders - which appear in patterns and values are the "clean" values to be - substituted into the pattern. Call pathauto_cleanstring() on any - values which you do not know to be purely alphanumeric, to substitute - any non-alphanumerics with the user's designated separator. Note that - if the pattern has multiple slash-separated components (e.g., [term:path]), - pathauto_cleanstring() should be called for each component, not the - complete string. - Example: $placeholders[t('[title]')] = pathauto_cleanstring($node->title); -$src - The "real" URI of the content to be aliased (e.g., "node/$node->nid") -$type - For modules which provided patternitems in hook_autopath(), - the relevant identifier for the specific item to be aliased (e.g., - $node->type) - -pathauto_create_alias() returns the alias that was created. - - -================== -3 - Bulk update function -================== - -If a module supports bulk updating of aliases, it must provide a -function of this form, to be called by pathauto when the corresponding -checkbox is selected and the settings page submitted: - -function _pathauto_bulkupdate() - -The function should iterate over the content items controlled by the -module, calling pathauto_create_alias() for each one. It is -recommended that the function report on its success (e.g., with a -count of created aliases) via drupal_set_message(). - - -================== -4 - Bulk delete hook_path_alias_types() -================== - -For modules that create new types of pages that can be aliased with pathauto, a -hook implementation is needed to allow the user to delete them all at once. - -function hook_path_alias_types() - -This hook returns an array whose keys match the beginning of the source paths -(e.g.: "node/", "user/", etc.) and whose values describe the type of page (e.g.: -"content", "users"). Like all displayed strings, these descriptionsshould be -localized with t(). Use % to match interior pieces of a path; "user/%/track". This -is a database wildcard, so be careful. - - -================== -Modules that extend node and/or taxonomy -================== - -NOTE: this is basically not true any more. If you feel you need this file an issue. - -Many contributed Drupal modules extend the core node and taxonomy -modules. To extend pathauto patterns to support their extensions, they -may implement the pathauto_node and pathauto_taxonomy hooks. - -To do so, implement the function _pathauto_node (or _taxonomy), -accepting the arguments $op and $node (or $term). Two operations are -supported: - -$op = 'placeholders' - return an array keyed on placeholder strings -(e.g., t('[eventyyyy]')) valued with descriptions (e.g. t('The year the -event starts.')). -$op = 'values' - return an array keyed on placeholder strings, valued -with the "clean" actual value for the passed node or category (e.g., -pathauto_cleanstring(date('M', $eventstart))); - -See contrib/pathauto_node_event.inc for an example of extending node -patterns. diff --git a/README.txt b/README.txt index 6bb138d..fc56959 100644 --- a/README.txt +++ b/README.txt @@ -3,18 +3,19 @@ They contain answers to many common questions. If you are developing for this module, the API.txt may be interesting. If you are upgrading, check the CHANGELOG.txt for major changes. -**Description: +** Description: The Pathauto module provides support functions for other modules to automatically generate aliases based on appropriate criteria, with a central settings path for site administrators. Implementations are provided for core entity types: content, taxonomy terms, -and users (including blogs and tracker pages). +and users (including blogs and forum pages). Pathauto also provides a way to delete large numbers of aliases. This feature -is available at Administer > Site building > URL aliases > Delete aliases +is available at Administer > Configuration > Search and metadata > URL aliases +> Delete aliases. -**Benefits: +** Benefits: Besides making the page address more reflective of its content than "node/138", it's important to know that modern search engines give heavy weight to search terms which appear in a page's URL. By @@ -22,10 +23,10 @@ automatically using keywords based directly on the page content in the URL, relevant search engine hits for your page can be significantly enhanced. -**Installation AND Upgrades: +** Installation AND Upgrades: See the INSTALL.txt file. -**Notices: +** Notices: Pathauto just adds URL aliases to content, users, and taxonomy terms. Because it's an alias, the standard Drupal URL (for example node/123 or taxonomy/term/1) will still function as normal. If you have external links @@ -62,25 +63,14 @@ When the pattern for a content type is left blank, the default pattern will be used. But if the default pattern is also blank, Pathauto will be disabled for that content type. -** Bulk Updates Must be Run Multiple Times: -As of 5.x-2.x Pathauto now performs bulk updates in a manner which is more -likely to succeed on large sites. The drawback is that it needs to be run -multiple times. If you want to reduce the number of times that you need to -run Pathauto you can increase the "Maximum number of objects to alias in a -bulk update:" setting under General Settings. - -**WYSIWYG Conflicts - FCKEditor, TinyMCE, etc. -If you use a WYSIWYG editor, please disable it for the Pathauto admin page. -Failure to do so may cause errors about "preg_replace" problems due to the

-tag being added to the "strings to replace". See http://drupal.org/node/175772 - -**Credits: +** Credits: The original module combined the functionality of Mike Ryan's autopath with Tommy Sundstrom's path_automatic. Significant enhancements were contributed by jdmquin @ www.bcdems.net. -Matt England added the tracker support. +Matt England added the tracker support (tracker support has been removed in +recent changes). Other suggestions and patches contributed by the Drupal community. diff --git a/pathauto.api.php b/pathauto.api.php index 37239cb..7d42a83 100644 --- a/pathauto.api.php +++ b/pathauto.api.php @@ -1,17 +1,119 @@ $type) { + $settings['patternitems'][$file_type] = t('Pattern for all @file_type paths.', array('@file_type' => $type->label)); + } + return (object) $settings; + + default: + break; + } } /** @@ -27,7 +129,7 @@ function hook_pathauto($op) { * @param string $langcode * The language code for the alias (e.g. 'en'). * - * @return + * @return bool * TRUE if $alias conflicts with an existing, reserved path, or FALSE/NULL if * it does not match any reserved paths. * @@ -56,7 +158,7 @@ function hook_pathauto_is_alias_reserved($alias, $source, $langcode) { * - 'language': A string of the language code for the alias (e.g. 'en'). * This can be altered by reference. */ -function hook_pathauto_pattern_alter(&$pattern, array &$context) { +function hook_pathauto_pattern_alter(&$pattern, array $context) { // Switch out any [node:created:*] tokens with [node:updated:*] on update. if ($context['module'] == 'node' && ($context['op'] == 'update')) { $pattern = preg_replace('/\[node:created(\:[^]]*)?\]/', '[node:updated$1]', $pattern); From 044e8285e8ae5c723699697df1600841e781ba87 Mon Sep 17 00:00:00 2001 From: davereid Date: Tue, 17 Mar 2015 14:50:23 -0500 Subject: [PATCH 005/169] Issue #1784874 by Dave Reid, aaronbauman: Converted admin UI to use the token dialog. --- pathauto.admin.inc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pathauto.admin.inc b/pathauto.admin.inc index b8679dd..b210386 100644 --- a/pathauto.admin.inc +++ b/pathauto.admin.inc @@ -64,17 +64,11 @@ function pathauto_patterns_form($form, $form_state) { } } - // Display the user documentation of placeholders supported by - // this module, as a description on the last pattern + // Show the token help relevant to this pattern type. $form[$module]['token_help'] = array( - '#title' => t('Replacement patterns'), - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $form[$module]['token_help']['help'] = array( '#theme' => 'token_tree', '#token_types' => array($settings->token_type), + '#dialog' => TRUE, ); } From a4e5a878efb806bb471e549da55688a49bb7ff38 Mon Sep 17 00:00:00 2001 From: Rajib Paudyal Date: Wed, 18 Mar 2015 15:56:29 +0200 Subject: [PATCH 006/169] Naming test method code should be self documenting, removed useless comments --- tests/src/VerboseMessengerTest.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/src/VerboseMessengerTest.php b/tests/src/VerboseMessengerTest.php index 622de3c..729fffc 100644 --- a/tests/src/VerboseMessengerTest.php +++ b/tests/src/VerboseMessengerTest.php @@ -34,7 +34,6 @@ protected function setUp() { ->withAnyParameters() ->willReturn(TRUE); - // The messenger under test. $this->messenger = new VerboseMessenger($config_factory, $account); } @@ -43,16 +42,10 @@ protected function setUp() { * @covers ::addMessage */ public function testAddMessage() { - // Test adding a message. $this->assertTrue($this->messenger->addMessage("Test message"), "The message was added"); } - /** - * Tests add messages. - * @covers ::addMessage - */ - public function testNotAddMessage() { - // Test adding a message. + public function testDoNotAddMessageWhileBulkupdate() { $this->assertFalse($this->messenger->addMessage("Test message", "bulkupdate"), "The message was NOT added"); } } From 23a0d3449d5c47d494ae63a923916195b6f65821 Mon Sep 17 00:00:00 2001 From: Rajib Paudyal Date: Wed, 18 Mar 2015 17:09:13 +0200 Subject: [PATCH 007/169] Added back test cover annotation --- tests/src/VerboseMessengerTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/src/VerboseMessengerTest.php b/tests/src/VerboseMessengerTest.php index 729fffc..e71cb3f 100644 --- a/tests/src/VerboseMessengerTest.php +++ b/tests/src/VerboseMessengerTest.php @@ -45,6 +45,9 @@ public function testAddMessage() { $this->assertTrue($this->messenger->addMessage("Test message"), "The message was added"); } + /** + * @covers ::addMessage + */ public function testDoNotAddMessageWhileBulkupdate() { $this->assertFalse($this->messenger->addMessage("Test message", "bulkupdate"), "The message was NOT added"); } From 54725e51306412ae0918578ef4e0e66ed0789afe Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 2 May 2015 23:41:47 +0200 Subject: [PATCH 008/169] Add user alias type plugin --- .../pathauto/AliasType/AliasTypeBase.php | 7 ++ .../pathauto/AliasType/UserAliasType.php | 101 ++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/Plugin/pathauto/AliasType/UserAliasType.php diff --git a/src/Plugin/pathauto/AliasType/AliasTypeBase.php b/src/Plugin/pathauto/AliasType/AliasTypeBase.php index 174e936..2828bac 100644 --- a/src/Plugin/pathauto/AliasType/AliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/AliasTypeBase.php @@ -117,6 +117,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta ); } + /** + * {@inheritdoc} + */ + public function getPatterns() { + return []; + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php new file mode 100644 index 0000000..01b6608 --- /dev/null +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -0,0 +1,101 @@ +moduleHandler = $module_handler; + $this->languageManager = $language_manager; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('module_handler'), + $container->get('language_manager'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getPatternDescription() { + $this->t('Pattern for user account page paths'); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array('patternitems' => array('users/[user:name]')) + parent::defaultConfiguration(); + } + +} From ce1d66168f84a08b9cf630e39f33df6edbd7e860 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 2 May 2015 23:43:24 +0200 Subject: [PATCH 009/169] Update patterns form to use plugins --- src/Form/PathautoPatternsForm.php | 44 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/Form/PathautoPatternsForm.php b/src/Form/PathautoPatternsForm.php index a00d683..a8a6f13 100644 --- a/src/Form/PathautoPatternsForm.php +++ b/src/Form/PathautoPatternsForm.php @@ -69,16 +69,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { $config = $this->config('pathauto.pattern'); - $all_settings = \Drupal::moduleHandler()->invokeAll('pathauto', array('settings')); + foreach ($definitions as $id => $definition) { + /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ + $alias_type = $this->aliasTypeManager->createInstance($id); - foreach ($all_settings as $settings) { - $module = $settings->module; - $patterndescr = $settings->patterndescr; - $groupheader = $settings->groupheader; - - $form[$module] = array( + $form[$id] = array( '#type' => 'fieldset', - '#title' => $groupheader, + '#title' => $alias_type->getLabel(), '#collapsible' => TRUE, '#collapsed' => FALSE, '#tree' => TRUE, @@ -87,33 +84,33 @@ public function buildForm(array $form, FormStateInterface $form_state) { // Prompt for the default pattern for this module. $key = 'default'; - $form[$module][$key] = array( + $form[$id][$key] = array( '#type' => 'textfield', - '#title' => $patterndescr, - '#default_value' => $config->get('patterns.' . $module . '.' . $key), + '#title' => $alias_type->getPatternDescription(), + '#default_value' => $config->get('patterns.' . $id . '.' . $key), '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), '#after_build' => array('token_element_validate'), - '#token_types' => array($settings->token_type), + '#token_types' => $alias_type->getTokenTypes(), '#min_tokens' => 1, ); // If the module supports a set of specialized patterns, set // them up here. - if (isset($settings->patternitems)) { - foreach ($settings->patternitems as $itemname => $itemlabel) { + if ($alias_type->getPatterns()) { + foreach ($alias_type->getPatterns() as $itemname => $itemlabel) { $key = 'default'; - $form[$module]['bundles'][$itemname][$key] = array( + $form[$id]['bundles'][$itemname][$key] = array( '#type' => 'textfield', '#title' => $itemlabel, - '#default_value' => $config->get('patterns.'. $module . '.bundles.' . $itemname . '.' . $key), + '#default_value' => $config->get('patterns.'. $id . '.bundles.' . $itemname . '.' . $key), '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), '#after_build' => array('token_element_validate'), - '#token_types' => array($settings->token_type), + '#token_types' => $alias_type->getTokenTypes(), '#min_tokens' => 1, ); } @@ -121,15 +118,15 @@ public function buildForm(array $form, FormStateInterface $form_state) { // Display the user documentation of placeholders supported by // this module, as a description on the last pattern. - $form[$module]['token_help'] = array( + $form[$id]['token_help'] = array( '#title' => t('Replacement patterns'), '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, ); - $form[$module]['token_help']['help'] = array( + $form[$id]['token_help']['help'] = array( '#theme' => 'token_tree', - '#token_types' => array($settings->token_type), + '#token_types' => $alias_type->getTokenTypes(), ); } @@ -143,11 +140,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $config = $this->config('pathauto.pattern'); - $all_settings = \Drupal::moduleHandler()->invokeAll('pathauto', array('settings')); + $definitions = $this->aliasTypeManager->getDefinitions(); - foreach ($all_settings as $settings) { - $module = $settings->module; - $config->set('patterns.' . $module, $form_state->getValue($module)); + foreach ($definitions as $id => $definition) { + $config->set('patterns.' . $id, $form_state->getValue($id)); } $config->save(); From 5d35c14b1ea9a013e8fa23abd3405a0912d1e709 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 2 May 2015 23:49:34 +0200 Subject: [PATCH 010/169] Added forum alias type --- .../pathauto/AliasType/AliasTypeBase.php | 2 +- .../pathauto/AliasType/ForumAliasType.php | 101 ++++++++++++++++++ .../pathauto/AliasType/NodeAliasType.php | 2 +- .../pathauto/AliasType/UserAliasType.php | 2 +- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/Plugin/pathauto/AliasType/ForumAliasType.php diff --git a/src/Plugin/pathauto/AliasType/AliasTypeBase.php b/src/Plugin/pathauto/AliasType/AliasTypeBase.php index 2828bac..39e62cd 100644 --- a/src/Plugin/pathauto/AliasType/AliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/AliasTypeBase.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\pathauto\Plugin\AliasType\AliasTypeBase + * Contains \Drupal\pathauto\Plugin\AliasType\AliasTypeBase. */ namespace Drupal\pathauto\Plugin\pathauto\AliasType; diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php new file mode 100644 index 0000000..98a0f8c --- /dev/null +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -0,0 +1,101 @@ +moduleHandler = $module_handler; + $this->languageManager = $language_manager; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('module_handler'), + $container->get('language_manager'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getPatternDescription() { + $this->t('Pattern for forums and forum containers'); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + } + +} diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 6c6ff0e..b807ab6 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\pathauto\Plugin\AliasType\NodeAliasType + * Contains \Drupal\pathauto\Plugin\AliasType\NodeAliasType. */ namespace Drupal\pathauto\Plugin\pathauto\AliasType; diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index 01b6608..fc657e3 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\pathauto\Plugin\AliasType\NodeAliasType + * Contains \Drupal\pathauto\Plugin\AliasType\UserAliasType. */ namespace Drupal\pathauto\Plugin\pathauto\AliasType; From b8c9f95a22b82307a0a25fea4f6d919aecd7ba07 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 00:02:19 +0200 Subject: [PATCH 011/169] Added term alias type plugin --- .../AliasType/TaxonomyTermAliasType.php | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php new file mode 100644 index 0000000..0ba4a24 --- /dev/null +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -0,0 +1,146 @@ +moduleHandler = $module_handler; + $this->languageManager = $language_manager; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('module_handler'), + $container->get('language_manager'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getPatternDescription() { + $this->t('Default path pattern (applies to all vocabularies with blank patterns below)'); + } + + /** + * {@inheritdoc} + */ + public function getPatterns() { + $patterns = []; + $languages = $this->languageManager->getLanguages(); + foreach ($this->getVocabularyNames() as $vid => $name) { + if (count($languages) && $this->isContentTranslationEnabled($vid)) { + $patterns[$vid] = $this->t('Default path pattern for %vocab-name (applies to all %vocab-name vocabularies with blank patterns below)', array('@node_type' => $name)); + foreach ($languages as $language) { + $patterns[$vid . '_' . $language->getId()] = t('Pattern for all @language %vocab-name paths', array('%vocab-name' => $name, '@language' => $language->getName())); + } + } + else { + $patterns[$vid] = t('Pattern for all %vocab-name paths', array('%vocab-name' => $name)); + } + } + return $patterns; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + } + + /** + * Returns vocabulary names. + * + * @return array + * An array of node type names, keyed by type. + */ + protected function getVocabularyNames() { + return array_map(function ($bundle_info) { + return $bundle_info['label']; + }, $this->entityManager->getBundleInfo('taxonomy_term')); + } + + /** + * Checks if content translation is neabled. + * + * @param string $vocabulary + * The vocabulary ID. + * + * @return bool + * TRUE if content translation is enabled for the vocabulary. + */ + protected function isContentTranslationEnabled($vocabulary) { + return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled('taxonomy_term', $vocabulary); + } + +} From 57b4934e6214ecf35d25b576331fa640e5e0169e Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 00:23:55 +0200 Subject: [PATCH 012/169] Move batch update methods to an additional interface --- src/AliasTypeBatchUpdateInterface.php | 22 +++++++ src/Form/PathautoBulkUpdateForm.php | 60 +++++++++++++------ .../pathauto/AliasType/ForumAliasType.php | 48 ++++++++++++++- .../pathauto/AliasType/NodeAliasType.php | 45 +++++++++++++- .../AliasType/TaxonomyTermAliasType.php | 46 ++++++++++++++ .../pathauto/AliasType/UserAliasType.php | 47 ++++++++++++++- 6 files changed, 246 insertions(+), 22 deletions(-) create mode 100644 src/AliasTypeBatchUpdateInterface.php diff --git a/src/AliasTypeBatchUpdateInterface.php b/src/AliasTypeBatchUpdateInterface.php new file mode 100644 index 0000000..7d64d96 --- /dev/null +++ b/src/AliasTypeBatchUpdateInterface.php @@ -0,0 +1,22 @@ +aliasTypeManager = $alias_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.alias_type') + ); + } + /** * {@inheritdoc} */ @@ -38,12 +67,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => array(), ); - $pathauto_settings = \Drupal::moduleHandler()->invokeAll('pathauto', array('settings')); + $definitions = $this->aliasTypeManager->getDefinitions(); - foreach ($pathauto_settings as $settings) { - if (!empty($settings->batch_update_callback)) { - $form['#update_callbacks'][$settings->batch_update_callback] = $settings; - $form['update']['#options'][$settings->batch_update_callback] = $settings->groupheader; + foreach ($definitions as $id => $definition) { + $alias_type = $this->aliasTypeManager->createInstance($id); + if ($alias_type instanceof AliasTypeBatchUpdateInterface) { + $form['update']['#options'][$id] = $alias_type->getLabel(); } } @@ -68,15 +97,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) { 'finished' => 'Drupal\pathauto\Form\PathautoBulkUpdateForm::batchFinished', ); - foreach ($form_state->getValue('update') as $callback) { - if (!empty($callback)) { - $settings = $form['#update_callbacks'][$callback]; - if (!empty($settings->batch_file)) { - $batch['operations'][] = array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchProcess', array($callback, $settings)); - } - else { - $batch['operations'][] = array($callback, array()); - } + foreach ($form_state->getValue('update') as $id) { + if (!empty($id)) { + $batch['operations'][] = array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchProcess', array($id)); } } @@ -95,11 +118,10 @@ public static function batchStart(&$context) { * * Required to load our include the proper batch file. */ - public static function batchProcess($callback, $settings, &$context) { - if (!empty($settings->batch_file)) { - require_once DRUPAL_ROOT . '/' . $settings->batch_file; - } - return $callback($context); + public static function batchProcess($id, &$context) { + /** @var \Drupal\pathauto\AliasTypeBatchUpdateInterface $alias_type */ + $alias_type = \Drupal::service('plugin.manager.alias_type')->createInstance($id); + $alias_type->batchUpdate($context); } /** diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 98a0f8c..026ba72 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -12,6 +12,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\pathauto\AliasTypeBatchUpdateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -24,7 +25,7 @@ * provider = "forum", * ) */ -class UserAliasType extends AliasTypeBase implements ContainerFactoryPluginInterface { +class UserAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -98,4 +99,49 @@ public function defaultConfiguration() { return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } + /** + * {@inheritdoc} + */ + public function batchUpdate(&$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + $query = db_select('taxonomy_term_data', 'td'); + $query->leftJoin('url_alias', 'ua', "CONCAT('forum/', td.tid) = ua.source"); + $query->addField('td', 'tid'); + $query->isNull('ua.source'); + $query->condition('td.tid', $context['sandbox']['current'], '>'); + $query->condition('td.vid', 'forums'); + $query->orderBy('td.tid'); + $query->addTag('pathauto_bulk_update'); + $query->addMetaData('entity', 'taxonomy_term'); + + // Get the total amount of items to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->countQuery() + ->execute() + ->fetchField(); + + // If there are no nodes to update, the stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + $query->range(0, 25); + $tids = $query->execute()->fetchCol(); + + pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); + $context['sandbox']['count'] += count($tids); + $context['sandbox']['current'] = max($tids); + $context['message'] = t('Updated alias for forum @tid.', array('@tid' => end($tids))); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } + } + } diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index b807ab6..f2b7118 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -12,6 +12,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\pathauto\AliasTypeBatchUpdateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -24,7 +25,7 @@ * provider = "node", * ) */ -class NodeAliasType extends AliasTypeBase implements ContainerFactoryPluginInterface { +class NodeAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -118,6 +119,48 @@ public function defaultConfiguration() { return array('patternitems' => array('content/[node:title]')) + parent::defaultConfiguration(); } + /** + * {@inheritdoc} + */ + public function batchUpdate(&$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + $query = db_select('node', 'n'); + $query->leftJoin('url_alias', 'ua', "CONCAT('node/', n.nid) = ua.source"); + $query->addField('n', 'nid'); + $query->isNull('ua.source'); + $query->condition('n.nid', $context['sandbox']['current'], '>'); + $query->orderBy('n.nid'); + $query->addTag('pathauto_bulk_update'); + $query->addMetaData('entity', 'node'); + + // Get the total amount of items to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); + + // If there are no nodes to update, the stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + $query->range(0, 25); + $nids = $query->execute()->fetchCol(); + + pathauto_node_update_alias_multiple($nids, 'bulkupdate'); + $context['sandbox']['count'] += count($nids); + $context['sandbox']['current'] = max($nids); + $context['message'] = t('Updated alias for node @nid.', array('@nid' => end($nids))); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } + } + /** * Wraps node_type_get_names(). * diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 0ba4a24..2da12a5 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -118,6 +118,52 @@ public function defaultConfiguration() { return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } + /** + * {@inheritdoc} + */ + public function batchUpdate(&$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + $query = db_select('taxonomy_term_data', 'td'); + $query->leftJoin('url_alias', 'ua', "CONCAT('taxonomy/term/', td.tid) = ua.source"); + $query->addField('td', 'tid'); + $query->isNull('ua.source'); + $query->condition('td.tid', $context['sandbox']['current'], '>'); + // Exclude the forums terms. + if ($forum_vid = 'forums') { + $query->condition('td.vid', $forum_vid, '<>'); + } + $query->orderBy('td.tid'); + $query->addTag('pathauto_bulk_update'); + $query->addMetaData('entity', 'taxonomy_term'); + + // Get the total amount of items to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); + + // If there are no nodes to update, the stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + $query->range(0, 25); + $tids = $query->execute()->fetchCol(); + + pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); + $context['sandbox']['count'] += count($tids); + $context['sandbox']['current'] = max($tids); + $context['message'] = t('Updated alias for term @tid.', array('@tid' => end($tids))); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } + } + /** * Returns vocabulary names. * diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index fc657e3..911b527 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -12,6 +12,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\pathauto\AliasTypeBatchUpdateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -24,7 +25,7 @@ * provider = "user", * ) */ -class UserAliasType extends AliasTypeBase implements ContainerFactoryPluginInterface { +class UserAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -98,4 +99,48 @@ public function defaultConfiguration() { return array('patternitems' => array('users/[user:name]')) + parent::defaultConfiguration(); } + /** + * {@inheritdoc} + */ + public function batchUpdate(&$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + $query = db_select('users', 'u'); + $query->leftJoin('url_alias', 'ua', "CONCAT('user/', u.uid) = ua.source"); + $query->addField('u', 'uid'); + $query->isNull('ua.source'); + $query->condition('u.uid', $context['sandbox']['current'], '>'); + $query->orderBy('u.uid'); + $query->addTag('pathauto_bulk_update'); + $query->addMetaData('entity', 'user'); + + // Get the total amount of items to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->countQuery() + ->execute() + ->fetchField(); + + // If there are no nodes to update, the stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + $query->range(0, 25); + $uids = $query->execute()->fetchCol(); + + pathauto_user_update_alias_multiple($uids, 'bulkupdate', array()); + $context['sandbox']['count'] += count($uids); + $context['sandbox']['current'] = max($uids); + $context['message'] = t('Updated alias for user @uid.', array('@uid' => end($uids))); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } + } + } From 8c0de58631979eb41d4f6dd565142845e0956c0a Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 00:31:11 +0200 Subject: [PATCH 013/169] Remove old hook and batch update callbacks --- pathauto.module | 22 --- pathauto.pathauto.inc | 304 ------------------------------------------ 2 files changed, 326 deletions(-) diff --git a/pathauto.module b/pathauto.module index 7484750..4848fdd 100644 --- a/pathauto.module +++ b/pathauto.module @@ -36,7 +36,6 @@ define('PATHAUTO_IGNORE_WORDS', 'a, an, as, at, before, but, by, for, from, is, */ function pathauto_hook_info() { $hooks = array( - 'pathauto', 'path_alias_types', 'pathauto_pattern_alter', 'pathauto_alias_alter', @@ -45,27 +44,6 @@ function pathauto_hook_info() { return array_fill_keys($hooks, array('group' => 'pathauto')); } - -/** - * Implements hook_module_implements_alter(). - * - * Adds pathauto support for core modules. - */ -function pathauto_module_implements_alter(&$implementations, $hook) { - if (in_array($hook, array('pathauto'))) { - $modules = array('node', 'taxonomy', 'user', 'forum'); - foreach ($modules as $module) { - if (\Drupal::moduleHandler()->moduleExists($module)) { - $implementations[$module] = TRUE; - } - } - // Move pathauto.module to get included first since it is responsible for - // other modules. - unset($implementations['pathauto']); - $implementations = array_merge(array('pathauto' => 'pathauto'), $implementations); - } -} - /** * Implements hook_help(). */ diff --git a/pathauto.pathauto.inc b/pathauto.pathauto.inc index 00861ef..3706364 100644 --- a/pathauto.pathauto.inc +++ b/pathauto.pathauto.inc @@ -6,7 +6,6 @@ * * @ingroup pathauto */ -use Drupal\Core\Language\Language; /** * Implements hook_path_alias_types(). @@ -24,306 +23,3 @@ function pathauto_path_alias_types() { } return $objects; } - -/** - * Implements hook_pathauto(). - * - * This function is empty so that the other core module implementations can be - * defined in this file. This is because in pathauto_module_implements_alter() - * we add pathauto to be included first. The module system then peforms a - * check on any subsequent run if this function still exists. If this does not - * exist, than this file will not get included and the core implementations - * will never get run. - * - * @see pathauto_module_implements_alter() - */ -function pathauto_pathauto() { - // Empty hook; see the above comment. -} - -/** - * Implements hook_pathauto(). - */ -function node_pathauto($op) { - switch ($op) { - case 'settings': - $settings = array(); - $settings['module'] = 'node'; - $settings['token_type'] = 'node'; - $settings['groupheader'] = t('Content paths'); - $settings['patterndescr'] = t('Default path pattern (applies to all content types with blank patterns below)'); - $settings['patterndefault'] = 'content/[node:title]'; - $settings['batch_update_callback'] = 'node_pathauto_bulk_update_batch_process'; - $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; - - $languages = array(); - if (\Drupal::moduleHandler()->moduleExists('locale')) { - $languages = array(Language::LANGCODE_NOT_SPECIFIED => t('language neutral')) + \Drupal::languageManager()->getLanguages('name'); - } - - foreach (node_type_get_names() as $node_type => $node_name) { - if (count($languages) && \Drupal::moduleHandler()->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled('node', $node_type)) { - $settings['patternitems'][$node_type] = t('Default path pattern for @node_type (applies to all @node_type content types with blank patterns below)', array('@node_type' => $node_name)); - foreach ($languages as $lang_code => $lang_name) { - $settings['patternitems'][$node_type . '_' . $lang_code] = t('Pattern for all @language @node_type paths', array('@node_type' => $node_name, '@language' => $lang_name)); - } - } - else { - $settings['patternitems'][$node_type] = t('Pattern for all @node_type paths', array('@node_type' => $node_name)); - } - } - return (object) $settings; - - default: - break; - } -} - -/** - * Batch processing callback; Generate aliases for nodes. - */ -function node_pathauto_bulk_update_batch_process(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('node', 'n'); - $query->leftJoin('url_alias', 'ua', "CONCAT('node/', n.nid) = ua.source"); - $query->addField('n', 'nid'); - $query->isNull('ua.source'); - $query->condition('n.nid', $context['sandbox']['current'], '>'); - $query->orderBy('n.nid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'node'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $nids = $query->execute()->fetchCol(); - - pathauto_node_update_alias_multiple($nids, 'bulkupdate'); - $context['sandbox']['count'] += count($nids); - $context['sandbox']['current'] = max($nids); - $context['message'] = t('Updated alias for node @nid.', array('@nid' => end($nids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } -} - -/** - * Implements hook_pathauto(). - */ -function taxonomy_pathauto($op) { - switch ($op) { - case 'settings': - $settings = array(); - $settings['module'] = 'taxonomy_term'; - $settings['token_type'] = 'term'; - $settings['groupheader'] = t('Taxonomy term paths'); - $settings['patterndescr'] = t('Default path pattern (applies to all vocabularies with blank patterns below)'); - $settings['patterndefault'] = '[term:vocabulary]/[term:name]'; - $settings['batch_update_callback'] = 'taxonomy_pathauto_bulk_update_batch_process'; - $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; - - $vocabularies = entity_load_multiple('taxonomy_vocabulary'); - if (count($vocabularies)) { - $settings['patternitems'] = array(); - foreach ($vocabularies as $vid => $vocabulary) { - /*if ($vid == variable_get('forum_nav_vocabulary', '')) { - // Skip the forum vocabulary. - continue; - }*/ - - $settings['patternitems'][$vocabulary->id()] = t('Pattern for all %vocab-name paths', array('%vocab-name' => $vocabulary->label())); - } - } - return (object) $settings; - - default: - break; - } -} - -/** - * Batch processing callback; Generate aliases for taxonomy terms. - */ -function taxonomy_pathauto_bulk_update_batch_process(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('taxonomy_term_data', 'td'); - $query->leftJoin('url_alias', 'ua', "CONCAT('taxonomy/term/', td.tid) = ua.source"); - $query->addField('td', 'tid'); - $query->isNull('ua.source'); - $query->condition('td.tid', $context['sandbox']['current'], '>'); - // Exclude the forums terms. - if ($forum_vid = 'forums') { - $query->condition('td.vid', $forum_vid, '<>'); - } - $query->orderBy('td.tid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'taxonomy_term'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $tids = $query->execute()->fetchCol(); - - pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); - $context['sandbox']['count'] += count($tids); - $context['sandbox']['current'] = max($tids); - $context['message'] = t('Updated alias for term @tid.', array('@tid' => end($tids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } -} - -/** - * Implements hook_pathauto() for forum module. - */ -function forum_pathauto($op) { - switch ($op) { - case 'settings': - $settings = array(); - $settings['module'] = 'forum'; - $settings['token_type'] = 'term'; - $settings['groupheader'] = t('Forum paths'); - $settings['patterndescr'] = t('Pattern for forums and forum containers'); - $settings['patterndefault'] = '[term:vocabulary]/[term:name]'; - $settings['batch_update_callback'] = 'forum_pathauto_bulk_update_batch_process'; - $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; - return (object) $settings; - - default: - break; - } -} - -/** - * Batch processing callback; Generate aliases for forums. - */ -function forum_pathauto_bulk_update_batch_process(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('taxonomy_term_data', 'td'); - $query->leftJoin('url_alias', 'ua', "CONCAT('forum/', td.tid) = ua.source"); - $query->addField('td', 'tid'); - $query->isNull('ua.source'); - $query->condition('td.tid', $context['sandbox']['current'], '>'); - $query->condition('td.vid', 'forums'); - $query->orderBy('td.tid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'taxonomy_term'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $tids = $query->execute()->fetchCol(); - - pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); - $context['sandbox']['count'] += count($tids); - $context['sandbox']['current'] = max($tids); - $context['message'] = t('Updated alias for forum @tid.', array('@tid' => end($tids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } -} - -/** - * Implements hook_pathauto(). - */ -function user_pathauto($op) { - switch ($op) { - case 'settings': - $settings = array(); - $settings['module'] = 'user'; - $settings['token_type'] = 'user'; - $settings['groupheader'] = t('User paths'); - $settings['patterndescr'] = t('Pattern for user account page paths'); - $settings['patterndefault'] = 'users/[user:name]'; - $settings['batch_update_callback'] = 'user_pathauto_bulk_update_batch_process'; - $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; - return (object) $settings; - - default: - break; - } -} - -/** - * Batch processing callback; Generate aliases for users. - */ -function user_pathauto_bulk_update_batch_process(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('users', 'u'); - $query->leftJoin('url_alias', 'ua', "CONCAT('user/', u.uid) = ua.source"); - $query->addField('u', 'uid'); - $query->isNull('ua.source'); - $query->condition('u.uid', $context['sandbox']['current'], '>'); - $query->orderBy('u.uid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'user'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $uids = $query->execute()->fetchCol(); - - pathauto_user_update_alias_multiple($uids, 'bulkupdate', array()); - $context['sandbox']['count'] += count($uids); - $context['sandbox']['current'] = max($uids); - $context['message'] = t('Updated alias for user @uid.', array('@uid' => end($uids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } -} From 26410298506dace7a0efc6e228a1d16387994c94 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 00:50:04 +0200 Subject: [PATCH 014/169] Use the build configuration form method --- src/Form/PathautoPatternsForm.php | 60 +------------------ .../pathauto/AliasType/AliasTypeBase.php | 39 ++++++------ .../pathauto/AliasType/ForumAliasType.php | 2 +- .../pathauto/AliasType/NodeAliasType.php | 2 +- .../AliasType/TaxonomyTermAliasType.php | 2 +- .../pathauto/AliasType/UserAliasType.php | 2 +- 6 files changed, 27 insertions(+), 80 deletions(-) diff --git a/src/Form/PathautoPatternsForm.php b/src/Form/PathautoPatternsForm.php index a8a6f13..c327df3 100644 --- a/src/Form/PathautoPatternsForm.php +++ b/src/Form/PathautoPatternsForm.php @@ -71,63 +71,9 @@ public function buildForm(array $form, FormStateInterface $form_state) { foreach ($definitions as $id => $definition) { /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ - $alias_type = $this->aliasTypeManager->createInstance($id); - - $form[$id] = array( - '#type' => 'fieldset', - '#title' => $alias_type->getLabel(), - '#collapsible' => TRUE, - '#collapsed' => FALSE, - '#tree' => TRUE, - ); - - // Prompt for the default pattern for this module. - $key = 'default'; - - $form[$id][$key] = array( - '#type' => 'textfield', - '#title' => $alias_type->getPatternDescription(), - '#default_value' => $config->get('patterns.' . $id . '.' . $key), - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $alias_type->getTokenTypes(), - '#min_tokens' => 1, - ); - - // If the module supports a set of specialized patterns, set - // them up here. - if ($alias_type->getPatterns()) { - foreach ($alias_type->getPatterns() as $itemname => $itemlabel) { - $key = 'default'; - - $form[$id]['bundles'][$itemname][$key] = array( - '#type' => 'textfield', - '#title' => $itemlabel, - '#default_value' => $config->get('patterns.'. $id . '.bundles.' . $itemname . '.' . $key), - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $alias_type->getTokenTypes(), - '#min_tokens' => 1, - ); - } - } - - // Display the user documentation of placeholders supported by - // this module, as a description on the last pattern. - $form[$id]['token_help'] = array( - '#title' => t('Replacement patterns'), - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $form[$id]['token_help']['help'] = array( - '#theme' => 'token_tree', - '#token_types' => $alias_type->getTokenTypes(), - ); + $alias_type = $this->aliasTypeManager->createInstance($id, $config->get('patterns.' . $id)); + + $form[$id] = $alias_type->buildConfigurationForm([], $form_state); } return parent::buildForm($form, $form_state); diff --git a/src/Plugin/pathauto/AliasType/AliasTypeBase.php b/src/Plugin/pathauto/AliasType/AliasTypeBase.php index 39e62cd..c4c0786 100644 --- a/src/Plugin/pathauto/AliasType/AliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/AliasTypeBase.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\pathauto\AliasTypeInterface; @@ -27,7 +28,10 @@ public function getConfiguration() { * {@inheritdoc} */ public function setConfiguration(array $configuration) { - $this->configuration = $configuration; + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); } /** @@ -59,28 +63,25 @@ public function getTokenTypes() { * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $plugin_id = $this->getPluginId(); - - $form[$plugin_id] = array( - '#type' => 'fieldset', + $form = array( + '#type' => 'details', '#title' => $this->getLabel(), - '#collapsible' => TRUE, - '#collapsed' => FALSE, + '#open' => TRUE, '#tree' => TRUE, ); // Prompt for the default pattern for this module. $key = '_default'; - $form[$plugin_id][$key] = array( + $form[$key] = array( '#type' => 'textfield', '#title' => $this->getPatternDescription(), - '#default_value' => $this->configuration['patternitems'], + '#default_value' => $this->configuration['default'], '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), '#after_build' => array('token_element_validate'), - '#token_types' => array($this->getTokenTypes()), + '#token_types' => $this->getTokenTypes(), '#min_tokens' => 1, ); @@ -90,31 +91,31 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta foreach ($patterns as $itemname => $itemlabel) { $key = '_default'; - $form[$plugin_id][$itemname][$key] = array( + $form[$itemname][$key] = array( '#type' => 'textfield', '#title' => $itemlabel, - '#default_value' => $this->configuration[$plugin_id . '.' . $itemname . '.' . $key], + '#default_value' => isset($this->configuration[$itemname . '.' . $key]) ? $this->configuration[$itemname . '.' . $key] : NULL, '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), '#after_build' => array('token_element_validate'), - '#token_types' => array($this->getTokenTypes()), + '#token_types' => $this->getTokenTypes(), '#min_tokens' => 1, ); } // Display the user documentation of placeholders supported by // this module, as a description on the last pattern. - $form[$plugin_id]['token_help'] = array( + $form['token_help'] = array( '#title' => t('Replacement patterns'), - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#type' => 'details', + '#open' => FALSE, ); - $form[$plugin_id]['token_help']['help'] = array( + $form['token_help']['help'] = array( '#theme' => 'token_tree', - '#token_types' => array($this->getTokenTypes()), + '#token_types' => $this->getTokenTypes(), ); + return $form; } /** diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 026ba72..6d88726 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -96,7 +96,7 @@ public function getPatternDescription() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + return array('default' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index f2b7118..1adfebb 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -116,7 +116,7 @@ public function getPatterns() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('patternitems' => array('content/[node:title]')) + parent::defaultConfiguration(); + return array('default' => array('content/[node:title]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 2da12a5..79b8aa9 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -115,7 +115,7 @@ public function getPatterns() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('patternitems' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + return array('default' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index 911b527..ffb6133 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -96,7 +96,7 @@ public function getPatternDescription() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('patternitems' => array('users/[user:name]')) + parent::defaultConfiguration(); + return array('default' => array('users/[user:name]')) + parent::defaultConfiguration(); } /** From bd73e270e7699edf69c7e4df3fe7a6e35a4164eb Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 12:11:48 +0200 Subject: [PATCH 015/169] Convert the alias type hook to a method on the alias type plugin --- pathauto.pathauto.inc | 25 ------ src/AliasTypeInterface.php | 8 ++ src/Form/PathautoAdminDelete.php | 86 +++++++++++-------- .../pathauto/AliasType/ForumAliasType.php | 10 ++- .../pathauto/AliasType/NodeAliasType.php | 7 ++ .../AliasType/TaxonomyTermAliasType.php | 11 ++- .../pathauto/AliasType/UserAliasType.php | 8 +- 7 files changed, 90 insertions(+), 65 deletions(-) delete mode 100644 pathauto.pathauto.inc diff --git a/pathauto.pathauto.inc b/pathauto.pathauto.inc deleted file mode 100644 index 3706364..0000000 --- a/pathauto.pathauto.inc +++ /dev/null @@ -1,25 +0,0 @@ -moduleExists('taxonomy')) { - $objects['taxonomy/term/'] = t('Taxonomy terms'); - } - if (\Drupal::moduleHandler()->moduleExists('forum')) { - $objects['forum/'] = t('Forums'); - } - return $objects; -} diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index d8297a5..3fbf7a4 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -47,4 +47,12 @@ public function getPatterns(); */ public function getTokenTypes(); + /** + * Returns the source prefix; used for bulk delete. + * + * @return string + * The source path prefix. + */ + public function getSourcePrefix(); + } diff --git a/src/Form/PathautoAdminDelete.php b/src/Form/PathautoAdminDelete.php index 0df7747..af8e302 100644 --- a/src/Form/PathautoAdminDelete.php +++ b/src/Form/PathautoAdminDelete.php @@ -9,12 +9,40 @@ use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\pathauto\AliasTypeManager; +use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Configure file system settings for this site. + * Alias mass delete form. */ class PathautoAdminDelete extends FormBase { + /** + * The alias type manager. + * + * @var \Drupal\pathauto\AliasTypeManager + */ + protected $aliasTypeManager; + + /** + * Constructs a PathautoPatternsForm object. + * + * @param \Drupal\pathauto\AliasTypeManager $alias_type_manager + * The alias type manager. + */ + public function __construct(AliasTypeManager $alias_type_manager) { + $this->aliasTypeManager = $alias_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.alias_type') + ); + } + /** * {@inheritdoc} */ @@ -26,14 +54,10 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - - $form = array(); - $form['delete'] = array( '#type' => 'fieldset', '#title' => t('Choose aliases to delete'), - '#collapsible' => FALSE, - '#collapsed' => FALSE, + '#tree' => TRUE, ); // First we do the "all" case. @@ -45,20 +69,18 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => t('Delete all aliases. Number of aliases which will be deleted: %count.', array('%count' => $total_count)), ); - // Next, iterate over an array of objects/alias types - // which can be deleted and provide checkboxes. - $args = func_get_args(); - // Remove $hook from the arguments. - unset($args[0]); - $objects = \Drupal::moduleHandler()->invokeAll('path_alias_types', $args); + // Next, iterate over all alias types + $definitions = $this->aliasTypeManager->getDefinitions(); - foreach ($objects as $internal_name => $label) { - $count = db_query("SELECT count(1) FROM {url_alias} WHERE source LIKE :src", array(':src' => "$internal_name%"))->fetchField(); - $form['delete'][$internal_name] = array( + foreach ($definitions as $id => $definition) { + /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ + $alias_type = $this->aliasTypeManager->createInstance($id); + $count = db_query("SELECT count(1) FROM {url_alias} WHERE source LIKE :src", array(':src' => $alias_type->getSourcePrefix() . '%'))->fetchField(); + $form['delete']['plugins'][$id] = array( '#type' => 'checkbox', - '#title' => $label, // This label is sent through t() in the hard coded function where it is defined. + '#title' => (string) $definition['label'], '#default_value' => FALSE, - '#description' => t('Delete aliases for all @label. Number of aliases which will be deleted: %count.', array('@label' => $label, '%count' => $count)), + '#description' => t('Delete aliases for all @label. Number of aliases which will be deleted: %count.', array('@label' => (string) $definition['label'], '%count' => $count)), ); } @@ -76,24 +98,18 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - foreach ($form_state->getValues() as $key => $value) { - if ($value) { - if ($key === 'all_aliases') { - db_delete('url_alias') - ->execute(); - drupal_set_message(t('All of your path aliases have been deleted.')); - } - $args = func_get_args(); - // Remove $hook from the arguments. - unset($args[0]); - $objects = \Drupal::moduleHandler()->invokeAll('path_alias_types', $args); - if (array_key_exists($key, $objects)) { - db_delete('url_alias') - ->condition('source', db_like($key) . '%', 'LIKE') - ->execute(); - drupal_set_message(t('All of your %type path aliases have been deleted.', array('%type' => $objects[$key]))); - } - } + if ($form_state->getValue(['delete', 'all_aliases'])) { + db_delete('url_alias') + ->execute(); + drupal_set_message($this->t('All of your path aliases have been deleted.')); + } + foreach (array_keys(array_filter($form_state->getValue(['delete', 'plugins']))) as $id) { + /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ + $alias_type = $this->aliasTypeManager->createInstance($id); + db_delete('url_alias') + ->condition('source', db_like($alias_type->getSourcePrefix()) . '%', 'LIKE') + ->execute(); + drupal_set_message(t('All of your %label path aliases have been deleted.', array('%label' => $alias_type->getLabel()))); } $form_state->setRedirect('pathauto.bulk.update.form'); } diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 6d88726..f4790cb 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -9,7 +9,6 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; @@ -25,7 +24,7 @@ * provider = "forum", * ) */ -class UserAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { +class ForumAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -144,4 +143,11 @@ public function batchUpdate(&$context) { } } + /** + * {@inheritdoc} + */ + public function getSourcePrefix() { + return 'forum/'; + } + } diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 1adfebb..079db83 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -161,6 +161,13 @@ public function batchUpdate(&$context) { } } + /** + * {@inheritdoc} + */ + public function getSourcePrefix() { + return 'node/'; + } + /** * Wraps node_type_get_names(). * diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 79b8aa9..dde5412 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -9,9 +9,9 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\pathauto\AliasTypeBatchUpdateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -24,7 +24,7 @@ * provider = "taxonomy", * ) */ -class TaxonomyTermAliasType extends AliasTypeBase implements ContainerFactoryPluginInterface { +class TaxonomyTermAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -164,6 +164,13 @@ public function batchUpdate(&$context) { } } + /** + * {@inheritdoc} + */ + public function getSourcePrefix() { + return 'taxonomy/term/'; + } + /** * Returns vocabulary names. * diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index ffb6133..b2a3199 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -9,7 +9,6 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; @@ -143,4 +142,11 @@ public function batchUpdate(&$context) { } } + /** + * {@inheritdoc} + */ + public function getSourcePrefix() { + return 'user/'; + } + } From f96d33abf5b6c210cd1835b971f738ea0cb6e26c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 12:35:01 +0200 Subject: [PATCH 016/169] Fixing tests --- src/Plugin/pathauto/AliasType/AliasTypeBase.php | 7 +++---- src/Plugin/pathauto/AliasType/ForumAliasType.php | 2 +- src/Plugin/pathauto/AliasType/NodeAliasType.php | 3 +-- src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php | 2 +- src/Plugin/pathauto/AliasType/UserAliasType.php | 2 +- src/Tests/AliasType/NodeAliasTest.php | 4 ++-- src/Tests/PathautoBulkUpdateTest.php | 4 ++-- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/AliasTypeBase.php b/src/Plugin/pathauto/AliasType/AliasTypeBase.php index c4c0786..537e0d6 100644 --- a/src/Plugin/pathauto/AliasType/AliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/AliasTypeBase.php @@ -71,7 +71,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta ); // Prompt for the default pattern for this module. - $key = '_default'; + $key = 'default'; $form[$key] = array( '#type' => 'textfield', @@ -89,9 +89,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta // them up here. $patterns = $this->getPatterns(); foreach ($patterns as $itemname => $itemlabel) { - $key = '_default'; - - $form[$itemname][$key] = array( + $key = 'default'; + $form['bundles'][$itemname][$key] = array( '#type' => 'textfield', '#title' => $itemlabel, '#default_value' => isset($this->configuration[$itemname . '.' . $key]) ? $this->configuration[$itemname . '.' . $key] : NULL, diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index f4790cb..0ca7c7c 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -88,7 +88,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function getPatternDescription() { - $this->t('Pattern for forums and forum containers'); + return $this->t('Pattern for forums and forum containers'); } /** diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 079db83..4a0153a 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -9,7 +9,6 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; @@ -89,7 +88,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function getPatternDescription() { - $this->t('Default path pattern (applies to all content types with blank patterns below)'); + return $this->t('Default path pattern (applies to all content types with blank patterns below)'); } /** diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index dde5412..5e61132 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -88,7 +88,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function getPatternDescription() { - $this->t('Default path pattern (applies to all vocabularies with blank patterns below)'); + return $this->t('Default path pattern (applies to all vocabularies with blank patterns below)'); } /** diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index b2a3199..2dfa3b6 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -88,7 +88,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function getPatternDescription() { - $this->t('Pattern for user account page paths'); + return $this->t('Pattern for user account page paths'); } /** diff --git a/src/Tests/AliasType/NodeAliasTest.php b/src/Tests/AliasType/NodeAliasTest.php index 61f186e..d745482 100644 --- a/src/Tests/AliasType/NodeAliasTest.php +++ b/src/Tests/AliasType/NodeAliasTest.php @@ -45,8 +45,8 @@ public function testNodeAlias() { $default_config = $node_type->defaultConfiguration(); - $this->assertTrue(array_key_exists('patternitems', $default_config), "Patternitems key exists."); - $this->assertEqual($default_config['patternitems'][0], 'content/[node:title]', "Default content pattern matches."); + $this->assertTrue(array_key_exists('default', $default_config), "Default key exists."); + $this->assertEqual($default_config['default'][0], 'content/[node:title]', "Default content pattern matches."); } diff --git a/src/Tests/PathautoBulkUpdateTest.php b/src/Tests/PathautoBulkUpdateTest.php index 01cd7d4..f9f03ec 100644 --- a/src/Tests/PathautoBulkUpdateTest.php +++ b/src/Tests/PathautoBulkUpdateTest.php @@ -69,8 +69,8 @@ function testBulkUpdate() { // Bulk create aliases. $edit = array( - 'update[node_pathauto_bulk_update_batch_process]' => TRUE, - 'update[user_pathauto_bulk_update_batch_process]' => TRUE, + 'update[node]' => TRUE, + 'update[user]' => TRUE, ); $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); $this->assertText('Generated 7 URL aliases.'); // 5 nodes + 2 users From f9853a523450b6fb10f4aaa1cebb999b80608ced Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 13:01:51 +0200 Subject: [PATCH 017/169] Refactor base class to be entity type specific, make getPatterns() more generic --- .../pathauto/AliasType/AliasTypeBase.php | 145 ---------- .../AliasType/EntityAliasTypeBase.php | 250 ++++++++++++++++++ .../pathauto/AliasType/ForumAliasType.php | 65 +---- .../pathauto/AliasType/NodeAliasType.php | 109 +------- .../AliasType/TaxonomyTermAliasType.php | 109 +------- .../pathauto/AliasType/UserAliasType.php | 64 +---- 6 files changed, 258 insertions(+), 484 deletions(-) delete mode 100644 src/Plugin/pathauto/AliasType/AliasTypeBase.php create mode 100644 src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php diff --git a/src/Plugin/pathauto/AliasType/AliasTypeBase.php b/src/Plugin/pathauto/AliasType/AliasTypeBase.php deleted file mode 100644 index 537e0d6..0000000 --- a/src/Plugin/pathauto/AliasType/AliasTypeBase.php +++ /dev/null @@ -1,145 +0,0 @@ -configuration; - } - - /** - * {@inheritdoc} - */ - public function setConfiguration(array $configuration) { - $this->configuration = NestedArray::mergeDeep( - $this->defaultConfiguration(), - $configuration - ); - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return array(); - } - - /** - * {@inheritdoc} - */ - public function getLabel() { - $definition = $this->getPluginDefinition(); - // Cast the admin label to a string since it is an object. - // @see \Drupal\Core\StringTranslation\TranslationWrapper - return (string) $definition['label']; - } - - /** - * {@inheritdoc} - */ - public function getTokenTypes() { - $definition = $this->getPluginDefinition(); - return $definition['types']; - } - - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $form = array( - '#type' => 'details', - '#title' => $this->getLabel(), - '#open' => TRUE, - '#tree' => TRUE, - ); - - // Prompt for the default pattern for this module. - $key = 'default'; - - $form[$key] = array( - '#type' => 'textfield', - '#title' => $this->getPatternDescription(), - '#default_value' => $this->configuration['default'], - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $this->getTokenTypes(), - '#min_tokens' => 1, - ); - - // If the module supports a set of specialized patterns, set - // them up here. - $patterns = $this->getPatterns(); - foreach ($patterns as $itemname => $itemlabel) { - $key = 'default'; - $form['bundles'][$itemname][$key] = array( - '#type' => 'textfield', - '#title' => $itemlabel, - '#default_value' => isset($this->configuration[$itemname . '.' . $key]) ? $this->configuration[$itemname . '.' . $key] : NULL, - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $this->getTokenTypes(), - '#min_tokens' => 1, - ); - } - - // Display the user documentation of placeholders supported by - // this module, as a description on the last pattern. - $form['token_help'] = array( - '#title' => t('Replacement patterns'), - '#type' => 'details', - '#open' => FALSE, - ); - $form['token_help']['help'] = array( - '#theme' => 'token_tree', - '#token_types' => $this->getTokenTypes(), - ); - return $form; - } - - /** - * {@inheritdoc} - */ - public function getPatterns() { - return []; - } - - /** - * {@inheritdoc} - */ - public function calculateDependencies() { - } - - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { - } - - /** - * {@inheritdoc} - */ - public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - } - -} diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php new file mode 100644 index 0000000..2d9081e --- /dev/null +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -0,0 +1,250 @@ +moduleHandler = $module_handler; + $this->languageManager = $language_manager; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('module_handler'), + $container->get('language_manager'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + $definition = $this->getPluginDefinition(); + // Cast the admin label to a string since it is an object. + // @see \Drupal\Core\StringTranslation\TranslationWrapper + return (string) $definition['label']; + } + + /** + * {@inheritdoc} + */ + public function getTokenTypes() { + $definition = $this->getPluginDefinition(); + return $definition['types']; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = array( + '#type' => 'details', + '#title' => $this->getLabel(), + '#open' => TRUE, + '#tree' => TRUE, + ); + + // Prompt for the default pattern for this module. + $key = 'default'; + + $form[$key] = array( + '#type' => 'textfield', + '#title' => $this->getPatternDescription(), + '#default_value' => $this->configuration['default'], + '#size' => 65, + '#maxlength' => 1280, + '#element_validate' => array('token_element_validate'), + '#after_build' => array('token_element_validate'), + '#token_types' => $this->getTokenTypes(), + '#min_tokens' => 1, + ); + + // If the module supports a set of specialized patterns, set + // them up here. + $patterns = $this->getPatterns(); + foreach ($patterns as $itemname => $itemlabel) { + $key = 'default'; + $form['bundles'][$itemname][$key] = array( + '#type' => 'textfield', + '#title' => $itemlabel, + '#default_value' => isset($this->configuration[$itemname . '.' . $key]) ? $this->configuration[$itemname . '.' . $key] : NULL, + '#size' => 65, + '#maxlength' => 1280, + '#element_validate' => array('token_element_validate'), + '#after_build' => array('token_element_validate'), + '#token_types' => $this->getTokenTypes(), + '#min_tokens' => 1, + ); + } + + // Display the user documentation of placeholders supported by + // this module, as a description on the last pattern. + $form['token_help'] = array( + '#title' => t('Replacement patterns'), + '#type' => 'details', + '#open' => FALSE, + ); + $form['token_help']['help'] = array( + '#theme' => 'token_tree', + '#token_types' => $this->getTokenTypes(), + ); + return $form; + } + + /** + * {@inheritdoc} + */ + public function getPatterns() { + $patterns = []; + $languages = $this->languageManager->getLanguages(); + if ($this->entityManager->getDefinition($this->getPluginId())->hasKey('bundle')) { + foreach ($this->getBundles() as $bundle => $bundle_label) { + if (count($languages) && $this->isContentTranslationEnabled($bundle)) { + $patterns[$bundle] = $this->t('Default path pattern for @bundle (applies to all @bundle fields with blank patterns below)', array('@bundle' => $bundle_label)); + foreach ($languages as $language) { + $patterns[$bundle . '_' . $language->getId()] = $this->t('Pattern for all @language @bundle paths', array( + '@bundle' => $bundle_label, + '@language' => $language->getName() + )); + } + } + else { + $patterns[$bundle] = $this->t('Pattern for all @bundle paths', array('@bundle' => $bundle_label)); + } + } + } + return $patterns; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * Returns bundles. + * + * @return string[] + * An array of bundle labels, keyed by bundle. + */ + protected function getBundles() { + return array_map(function ($bundle_info) { + return $bundle_info['label']; + }, $this->entityManager->getBundleInfo($this->getPluginId())); + } + + /** + * Checks if a bundle is enabled for translation. + * + * @param string $bundle + * The bundle. + * + * @return bool + * TRUE if content translation is enabled for the bundle. + */ + protected function isContentTranslationEnabled($bundle) { + return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled($this->getPluginId(), $bundle); + } + +} diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 0ca7c7c..1cd291c 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -7,12 +7,8 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * A pathauto alias type plugin for forum terms. @@ -24,71 +20,20 @@ * provider = "forum", * ) */ -class ForumAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The language manager service. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The entity manager service. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Constructs a NodeAliasType instance. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->moduleHandler = $module_handler; - $this->languageManager = $language_manager; - $this->entityManager = $entity_manager; - } +class ForumAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('module_handler'), - $container->get('language_manager'), - $container->get('entity.manager') - ); + public function getPatternDescription() { + return $this->t('Pattern for forums and forum containers'); } /** * {@inheritdoc} */ - public function getPatternDescription() { - return $this->t('Pattern for forums and forum containers'); + public function getPatterns() { + return []; } /** diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 4a0153a..5b701b3 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -7,12 +7,8 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * A pathauto alias type plugin for content entities. @@ -24,65 +20,7 @@ * provider = "node", * ) */ -class NodeAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The language manager service. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The entity manager service. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Constructs a NodeAliasType instance. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->moduleHandler = $module_handler; - $this->languageManager = $language_manager; - $this->entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('module_handler'), - $container->get('language_manager'), - $container->get('entity.manager') - ); - } +class NodeAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * {@inheritdoc} @@ -91,26 +29,6 @@ public function getPatternDescription() { return $this->t('Default path pattern (applies to all content types with blank patterns below)'); } - /** - * {@inheritdoc} - */ - public function getPatterns() { - $patterns = []; - $languages = $this->languageManager->getLanguages(); - foreach ($this->getNodeTypeNames() as $node_type => $node_type_name) { - if (count($languages) && $this->isContentTranslationEnabled($node_type)) { - $patterns[$node_type] = $this->t('Default path pattern for @node_type (applies to all @node_type content types with blank patterns below)', array('@node_type' => $node_type_name)); - foreach ($languages as $language) { - $patterns[$node_type . '_' . $language->getId()] = $this->t('Pattern for all @language @node_type paths', array('@node_type' => $node_type_name, '@language' => $language->getName())); - } - } - else { - $patterns[$node_type] = $this->t('Pattern for all @node_type paths', array('@node_type' => $node_type_name)); - } - } - return $patterns; - } - /** * {@inheritdoc} */ @@ -167,29 +85,4 @@ public function getSourcePrefix() { return 'node/'; } - /** - * Wraps node_type_get_names(). - * - * @return array - * An array of node type names, keyed by type. - */ - protected function getNodeTypeNames() { - return array_map(function ($bundle_info) { - return $bundle_info['label']; - }, $this->entityManager->getBundleInfo('node')); - } - - /** - * Wraps content_translation_enabled(). - * - * @param string $node_type - * The node type. - * - * @return bool - * TRUE if content translation is enabled for the content type. - */ - protected function isContentTranslationEnabled($node_type) { - return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled('node', $node_type); - } - } diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 5e61132..2056787 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -7,12 +7,8 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * A pathauto alias type plugin for taxonomy term entities. @@ -24,65 +20,7 @@ * provider = "taxonomy", * ) */ -class TaxonomyTermAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The language manager service. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The entity manager service. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Constructs a NodeAliasType instance. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->moduleHandler = $module_handler; - $this->languageManager = $language_manager; - $this->entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('module_handler'), - $container->get('language_manager'), - $container->get('entity.manager') - ); - } +class TaxonomyTermAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * {@inheritdoc} @@ -91,26 +29,6 @@ public function getPatternDescription() { return $this->t('Default path pattern (applies to all vocabularies with blank patterns below)'); } - /** - * {@inheritdoc} - */ - public function getPatterns() { - $patterns = []; - $languages = $this->languageManager->getLanguages(); - foreach ($this->getVocabularyNames() as $vid => $name) { - if (count($languages) && $this->isContentTranslationEnabled($vid)) { - $patterns[$vid] = $this->t('Default path pattern for %vocab-name (applies to all %vocab-name vocabularies with blank patterns below)', array('@node_type' => $name)); - foreach ($languages as $language) { - $patterns[$vid . '_' . $language->getId()] = t('Pattern for all @language %vocab-name paths', array('%vocab-name' => $name, '@language' => $language->getName())); - } - } - else { - $patterns[$vid] = t('Pattern for all %vocab-name paths', array('%vocab-name' => $name)); - } - } - return $patterns; - } - /** * {@inheritdoc} */ @@ -171,29 +89,4 @@ public function getSourcePrefix() { return 'taxonomy/term/'; } - /** - * Returns vocabulary names. - * - * @return array - * An array of node type names, keyed by type. - */ - protected function getVocabularyNames() { - return array_map(function ($bundle_info) { - return $bundle_info['label']; - }, $this->entityManager->getBundleInfo('taxonomy_term')); - } - - /** - * Checks if content translation is neabled. - * - * @param string $vocabulary - * The vocabulary ID. - * - * @return bool - * TRUE if content translation is enabled for the vocabulary. - */ - protected function isContentTranslationEnabled($vocabulary) { - return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled('taxonomy_term', $vocabulary); - } - } diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index 2dfa3b6..e918e0d 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -7,12 +7,8 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\pathauto\AliasTypeBatchUpdateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * A pathauto alias type plugin for user entities. @@ -24,65 +20,7 @@ * provider = "user", * ) */ -class UserAliasType extends AliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The language manager service. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The entity manager service. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Constructs a NodeAliasType instance. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->moduleHandler = $module_handler; - $this->languageManager = $language_manager; - $this->entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('module_handler'), - $container->get('language_manager'), - $container->get('entity.manager') - ); - } +class UserAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * {@inheritdoc} From 616eedb7df018bf2e73421f4b3640284fbbfc428 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 3 May 2015 13:14:59 +0200 Subject: [PATCH 018/169] Clean up hook documentation --- pathauto.api.php | 14 +++++++++----- pathauto.module | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pathauto.api.php b/pathauto.api.php index 26ec374..9f468ef 100644 --- a/pathauto.api.php +++ b/pathauto.api.php @@ -9,10 +9,14 @@ * @see hook_tokens */ -function hook_path_alias_types() { -} - -function hook_pathauto($op) { +/** + * Alter pathauto alias type definitions. + * + * @param array &$definitions + * Alias type definitions. + * + */ +function hook_path_alias_types_alter(array &$definitions) { } /** @@ -59,7 +63,7 @@ function hook_pathauto_is_alias_reserved($alias, $source, $langcode) { */ function hook_pathauto_pattern_alter(&$pattern, array &$context) { // Switch out any [node:created:*] tokens with [node:updated:*] on update. - if ($module == 'node' && ($context['op'] == 'update')) { + if ($context['module'] == 'node' && ($context['op'] == 'update')) { $pattern = preg_replace('/\[node:created(\:[^]]*)?\]/', '[node:updated$1]', $pattern); } } diff --git a/pathauto.module b/pathauto.module index 4848fdd..97919d4 100644 --- a/pathauto.module +++ b/pathauto.module @@ -36,7 +36,6 @@ define('PATHAUTO_IGNORE_WORDS', 'a, an, as, at, before, but, by, for, from, is, */ function pathauto_hook_info() { $hooks = array( - 'path_alias_types', 'pathauto_pattern_alter', 'pathauto_alias_alter', 'pathauto_is_alias_reserved', From d6c3f4e5fc460a7d2cfff1a3b9d6987d53585c7a Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Sun, 10 May 2015 14:02:38 -0300 Subject: [PATCH 019/169] Update PathautoManager.php $langcode is the second parameter --- src/PathautoManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 94cf490..7f50001 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -228,7 +228,7 @@ public function cleanString($string, array $options = array()) { // If the reduce strings to letters and numbers is enabled, don't bother // replacing unknown characters with a question mark. Use an empty string // instead. - $output = \Drupal::service('transliteration')->transliterate($output, $this->cleanStringCache['reduce_ascii'] ? '' : '?', $langcode); + $output = \Drupal::service('transliteration')->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?'); } // Replace or drop punctuation based on user settings. From bc83f2d6b4cad56b0ec27f3c7a5fb94c22cccc61 Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Sun, 10 May 2015 12:41:48 -0700 Subject: [PATCH 020/169] Move delete functions to storage helper --- pathauto.module | 53 +---------------------------- src/AliasStorageHelper.php | 39 +++++++++++++++++++++ src/AliasStorageHelperInterface.php | 45 ++++++++++++++++++++++++ src/Tests/PathautoUnitTest.php | 2 +- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/pathauto.module b/pathauto.module index 97919d4..18a8034 100644 --- a/pathauto.module +++ b/pathauto.module @@ -64,57 +64,6 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { } } -/** - * Delete multiple URL aliases. - * - * Intent of this is to abstract a potential path_delete_multiple() function - * for Drupal 7 or 8. - * - * @param $pids - * An array of path IDs to delete. - */ -function pathauto_path_delete_multiple($pids) { - foreach ($pids as $pid) { - \Drupal::service('path.alias_storage')->delete(array('pid' => $pid)); - } -} - -/** - * Delete an URL alias and any of its sub-paths. - * - * Given a source like 'node/1' this function will delete any alias that have - * that specific source or any sources that match 'node/1/%'. - * - * @param $source - * An string with a source URL path. - */ -function pathauto_path_delete_all($source) { - $sql = "SELECT pid FROM {url_alias} WHERE source = :source OR source LIKE :source_wildcard"; - $pids = db_query($sql, array(':source' => $source, ':source_wildcard' => $source . '/%'))->fetchCol(); - if ($pids) { - pathauto_path_delete_multiple($pids); - } -} - -/** - * Delete an entity URL alias and any of its sub-paths. - * - * This function also checks to see if the default entity URI is different from - * the current entity URI and will delete any of the default aliases. - * - * @param EntityInterface $entity - * An entity object. - * @param string $default_uri - * The optional default uri path for the entity. - */ -function pathauto_entity_path_delete_all(EntityInterface $entity, $default_uri = NULL) { - pathauto_path_delete_all($entity->getSystemPath()); - if (isset($default_uri) && $entity->getSystemPath() != $default_uri) { - pathauto_path_delete_all($default_uri); - } -} - - /** * Implements hook_entity_bundle_rename(). */ @@ -187,7 +136,7 @@ function pathauto_entity_update(EntityInterface $entity) { * Implements hook_entity_update(). */ function pathauto_entity_delete(EntityInterface $entity) { - pathauto_entity_path_delete_all($entity); + \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity); } /** diff --git a/src/AliasStorageHelper.php b/src/AliasStorageHelper.php index 2c4f1f1..2d848bd 100644 --- a/src/AliasStorageHelper.php +++ b/src/AliasStorageHelper.php @@ -9,6 +9,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Database\Connection; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Path\AliasStorageInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -175,4 +176,42 @@ public function exists($alias, $source, $language = LanguageInterface::LANGCODE_ ))->fetchField(); } + /** + * {@inheritdoc} + */ + public function deleteAll($source) { + $pids = $this->loadBySourcePrefix($source); + if ($pids) { + $this->deleteMultiple($pids); + } + } + + /** + * {@inheritdoc} + */ + public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL) { + $this->deleteAll($entity->urlInfo()->toString()); + if (isset($default_uri) && $entity->urlInfo()->toString() != $default_uri) { + $this->deleteAll($default_uri); + } + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($pids) { + foreach ($pids as $pid) { + $this->aliasStorage->delete(array('pid' => $pid)); + } + } + + /** + * {@inheritdoc} + */ + public function loadBySourcePrefix($source) { + return $this->database->query("SELECT pid FROM {url_alias} WHERE source = :source OR source LIKE :source_wildcard", + [':source' => $source, ':source_wildcard' => $source . '/%']) + ->fetchCol(); + } + } diff --git a/src/AliasStorageHelperInterface.php b/src/AliasStorageHelperInterface.php index 3ad7460..e4d9c6d 100644 --- a/src/AliasStorageHelperInterface.php +++ b/src/AliasStorageHelperInterface.php @@ -6,6 +6,7 @@ */ namespace Drupal\pathauto; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\LanguageInterface; /** @@ -71,4 +72,48 @@ public function loadBySource($source, $language = LanguageInterface::LANGCODE_NO */ public function exists($alias, $source, $language = LanguageInterface::LANGCODE_NOT_SPECIFIED); + /** + * Delete all aliases by source url. + * + * Can use wildcard patterns, e.g. + * + * @param string $source + * The URL alias source. + */ + public function deleteAll($source); + + /** + * Delete an entity URL alias and any of its sub-paths. + * + * This function also checks to see if the default entity URI is different + * from the current entity URI and will delete any of the default aliases. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * An entity object. + * @param string $default_uri + * The optional default uri path for the entity. + */ + public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL); + + /** + * Delete multiple URL aliases. + * + * Intent of this is to abstract a potential path_delete_multiple() function + * for Drupal 7 or 8. + * + * @param integer[] $pids + * An array of path IDs to delete. + */ + public function deleteMultiple($pids); + + /** + * Fetches an existing URL alias given a path prefix. + * + * @param string $source + * An internal Drupal path prefix. + * + * @return integer[] + * An array of PIDs. + */ + public function loadBySourcePrefix($source); } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 41a2015..fb18a16 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -180,7 +180,7 @@ public function testPathDeleteMultiple() { $this->saveAlias('node/1', 'node-1-alias-fr', 'fr'); $this->saveAlias('node/2', 'node-2-alias'); - pathauto_path_delete_all('node/1'); + \Drupal::service('pathauto.alias_storage_helper')->deleteAll('node/1'); $this->assertNoAliasExists(array('source' => "node/1")); $this->assertNoAliasExists(array('source' => "node/1/view")); $this->assertAliasExists(array('source' => "node/2")); From b0ea4c52c4fb8125e507fb2cec2fe3e91f358d17 Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Sun, 10 May 2015 13:12:38 -0700 Subject: [PATCH 021/169] Fix invalid settings value --- config/install/pathauto.settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/install/pathauto.settings.yml b/config/install/pathauto.settings.yml index bdae42e..2a331f2 100644 --- a/config/install/pathauto.settings.yml +++ b/config/install/pathauto.settings.yml @@ -7,6 +7,6 @@ max_component_length: 100 transliterate : FALSE reduce_ascii : FALSE ignore_words : ', in, is,that, the , this, with, ' -case : 1 +case : TRUE ignore_words : 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with' update_action : 2 From 77c2014fbe02e8838323ff75b3dc6293173eaf3b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 15 May 2015 19:53:53 +0200 Subject: [PATCH 022/169] entity and menu_link modules no longer exist --- src/Tests/PathautoUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index fb18a16..f5013c3 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -21,7 +21,7 @@ class PathautoUnitTest extends KernelTestBase { use PathautoTestHelperTrait; - public static $modules = array('system', 'entity', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'menu_link', 'filter'); + public static $modules = array('system', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'filter'); protected $currentUser; From da840448265247ca72db7661cceba61c374ec469 Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Wed, 20 May 2015 14:13:03 -0300 Subject: [PATCH 023/169] Patterns Form loses default values --- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 2d9081e..7277a91 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -155,7 +155,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form['bundles'][$itemname][$key] = array( '#type' => 'textfield', '#title' => $itemlabel, - '#default_value' => isset($this->configuration[$itemname . '.' . $key]) ? $this->configuration[$itemname . '.' . $key] : NULL, + '#default_value' => isset($this->configuration['bundles'][$itemname][$key]) ? $this->configuration['bundles'][$itemname][$key] : NULL, '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), From 392b379a7b4ee874f54c8524d5ee632715b5b692 Mon Sep 17 00:00:00 2001 From: Darryl Norris Date: Wed, 27 May 2015 07:31:40 -0700 Subject: [PATCH 024/169] Update TODO.txt Hook_help have been already added to this project. There not need to have it on the TODO.txt. --- TODO.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 710a830..7c5ca19 100644 --- a/TODO.txt +++ b/TODO.txt @@ -7,5 +7,3 @@ - pathauto_path_alias_types() => Plugins - INSTALL.txt: Update - -- Update hook_help(): https://drupal.org/node/2250345 From 387fedc6aba6a61c40027007066f3c7784e7dd9d Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 3 Jun 2015 23:06:52 +0200 Subject: [PATCH 025/169] Removed usage of getSystemPath() --- src/PathautoManager.php | 2 +- src/Plugin/Field/FieldWidget/PathautoWidget.php | 6 +++--- src/Tests/PathautoNodeWebTest.php | 4 ++-- src/Tests/PathautoTestHelperTrait.php | 10 +++++----- src/Tests/PathautoUnitTest.php | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 7f50001..7e18675 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -491,7 +491,7 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } $result = $this->createAlias( - $type, $op, $entity->getSystemPath(), array($type => $entity), $bundle, $options['language']); + $type, $op, $entity->urlInfo()->getInternalPath(), array($type => $entity), $bundle, $options['language']); if ($type == 'taxonomy_term' && empty($options['is_child'])) { // For all children generate new aliases. diff --git a/src/Plugin/Field/FieldWidget/PathautoWidget.php b/src/Plugin/Field/FieldWidget/PathautoWidget.php index 8acde06..87ec748 100644 --- a/src/Plugin/Field/FieldWidget/PathautoWidget.php +++ b/src/Plugin/Field/FieldWidget/PathautoWidget.php @@ -61,9 +61,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if (!isset($entity->path->pathauto)) { if (!$entity->isNew()) { module_load_include('inc', 'pathauto'); - $path = \Drupal::service('path.alias_manager')->getAliasByPath($entity->getSystemPath(), $entity->language()->getId()); - $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', $entity->getSystemPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); - $entity->path->pathauto = ($path != $entity->getSystemPath() && $path == $pathauto_alias); + $path = \Drupal::service('path.alias_manager')->getAliasByPath($entity->urlInfo()->getInternalPath(), $entity->language()->getId()); + $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', $entity->urlInfo()->getInternalPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); + $entity->path->pathauto = ($path != $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); } else { $entity->path->pathauto = TRUE; diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index 1e2626a..1f1d37d 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -82,7 +82,7 @@ function testNodeEditing() { 'path[0][pathauto]' => FALSE, 'path[0][alias]' => $manual_alias, ); - $this->drupalPostForm("{$node->getSystemPath()}/edit", $edit, t('Save and keep published')); + $this->drupalPostForm($node->urlInfo('edit-form'), $edit, t('Save and keep published')); $this->assertRaw(t('@type %title has been updated.', array('@type' => 'page', '%title' => $title))); // Check that the automatic alias checkbox is now unchecked by default. @@ -132,7 +132,7 @@ function testNodeEditing() { $node = $this->drupalGetNodeByTitle($edit['title']); // Pathauto checkbox should still not exist. - $this->drupalGet($node->getSystemPath() . '/edit'); + $this->drupalGet($node->urlInfo('edit-form')); $this->assertNoFieldById('edit-path-0-pathauto'); $this->assertFieldByName('path[0][alias]', ''); $this->assertNoEntityAlias($node); diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 304cb50..8e40265 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -33,7 +33,7 @@ public function saveEntityAlias(EntityInterface $entity, $alias, $langcode = NUL if (!$langcode) { $langcode = $entity->language()->getId(); } - return $this->saveAlias($entity->getSystemPath(), $alias, $langcode); + return $this->saveAlias($entity->urlInfo()->getInternalPath(), $alias, $langcode); } public function assertEntityAlias(EntityInterface $entity, $expected_alias, $langcode = NULL) { @@ -41,11 +41,11 @@ public function assertEntityAlias(EntityInterface $entity, $expected_alias, $lan if (!$langcode) { $langcode = $entity->language()->getId(); } - $this->assertAlias($entity->getSystemPath(), $expected_alias, $langcode); + $this->assertAlias($entity->urlInfo()->getInternalPath(), $expected_alias, $langcode); } public function assertEntityAliasExists(EntityInterface $entity) { - return $this->assertAliasExists(array('source' => $entity->getSystemPath())); + return $this->assertAliasExists(array('source' => $entity->urlInfo()->getInternalPath())); } public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) { @@ -53,11 +53,11 @@ public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) { if (!$langcode) { $langcode = $entity->language()->getId(); } - $this->assertEntityAlias($entity, $entity->getSystemPath(), $langcode); + $this->assertEntityAlias($entity, $entity->urlInfo()->getInternalPath(), $langcode); } public function assertNoEntityAliasExists(EntityInterface $entity) { - $this->assertNoAliasExists(array('source' => $entity->getSystemPath())); + $this->assertNoAliasExists(array('source' => $entity->urlInfo()->getInternalPath())); } public function assertAlias($source, $expected_alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index f5013c3..e816201 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -214,7 +214,7 @@ public function testUpdateActions() { $node->setTitle('Third title'); $node->save(); $this->assertEntityAlias($node, 'content/third-title'); - $this->assertAliasExists(array('source' => $node->getSystemPath(), 'alias' => 'content/second-title')); + $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); $config->save(); @@ -223,7 +223,7 @@ public function testUpdateActions() { $this->assertEntityAlias($node, 'content/fourth-title'); $this->assertNoAliasExists(array('alias' => 'content/third-title')); // The older second alias is not deleted yet. - $older_path = $this->assertAliasExists(array('source' => $node->getSystemPath(), 'alias' => 'content/second-title')); + $older_path = $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); \Drupal::service('path.alias_storage')->delete($older_path); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); From 33022e39f65da5682ed238af857e592ab3187c5f Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 19 Jun 2015 11:34:54 +0200 Subject: [PATCH 026/169] Replace removed taxonomy_get_tree() --- src/PathautoManager.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 7e18675..b5172d9 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -508,8 +508,6 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array /** * Create a hierarchical representation of a vocabulary. * - * Wrapper of taxonomy_get_tree() for testing. - * * @param int $vid * The vocabulary ID to generate the tree for. * @param int $parent @@ -530,7 +528,7 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array * depending on the $load_entities parameter. */ protected function getTermTree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) { - return taxonomy_get_tree($vid, $parent, $max_depth, $load_entities); + return \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities); } /** From 9a1753778464ff471a650d99689079df7c39bb2e Mon Sep 17 00:00:00 2001 From: rrfegade Date: Mon, 29 Jun 2015 16:11:57 -0500 Subject: [PATCH 027/169] Issue #2511988 by rrfegade: Fixed documentation spelling errors. --- pathauto.inc | 2 +- pathauto.module | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pathauto.inc b/pathauto.inc index 63b9244..39e50b6 100644 --- a/pathauto.inc +++ b/pathauto.inc @@ -170,7 +170,7 @@ function pathauto_cleanstring($string, array $options = array()) { } } - // Empty strings do not need any proccessing. + // Empty strings do not need any processing. if ($string === '' || $string === NULL) { return ''; } diff --git a/pathauto.module b/pathauto.module index 9bb132d..be085fb 100644 --- a/pathauto.module +++ b/pathauto.module @@ -413,7 +413,7 @@ function pathauto_action_info() { * Returns the language code of the given entity. * * Backward compatibility layer to ensure that installations running an older - * version of core where entity_language() is not avilable do not break. + * version of core where entity_language() is not available do not break. * * @param string $entity_type * An entity type. From 0253c54a90b8757a603ba657059dd2fcc6242c57 Mon Sep 17 00:00:00 2001 From: basvanderheijden Date: Mon, 29 Jun 2015 16:16:27 -0500 Subject: [PATCH 028/169] Issue #2351205 by basvanderheijden: Fixed Drupal.behaviors.pathFieldsetSummaries did not use context. --- pathauto.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pathauto.js b/pathauto.js index be49cd6..52dcf12 100644 --- a/pathauto.js +++ b/pathauto.js @@ -3,13 +3,13 @@ Drupal.behaviors.pathFieldsetSummaries = { attach: function (context) { $('fieldset.path-form', context).drupalSetSummary(function (context) { - var path = $('.form-item-path-alias input').val(); - var automatic = $('.form-item-path-pathauto input').attr('checked'); + var path = $('.form-item-path-alias input', context).val(); + var automatic = $('.form-item-path-pathauto input', context).attr('checked'); if (automatic) { return Drupal.t('Automatic alias'); } - if (path) { + else if (path) { return Drupal.t('Alias: @alias', { '@alias': path }); } else { From 579bb1b9790e97763b5c6b3f27151c764e9e4acc Mon Sep 17 00:00:00 2001 From: Lukas Schneider Date: Thu, 23 Apr 2015 15:06:55 +0200 Subject: [PATCH 029/169] Removed String class methods from module and replaced them with SafeMarkup/Html equivalents --- src/PathautoManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index b5172d9..c5e2c85 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -7,8 +7,8 @@ namespace Drupal\pathauto; -use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\ContentEntityInterface; From faaac8ac716b0f0aea1ff63fcff4c2054bec157e Mon Sep 17 00:00:00 2001 From: Lukas Schneider Date: Tue, 7 Jul 2015 15:00:22 +0200 Subject: [PATCH 030/169] added slashes to all aliases (creation and assertion) --- config/install/pathauto.pattern.yml | 8 +- src/AliasCleaner.php | 2 +- src/PathautoManager.php | 2 +- .../Field/FieldWidget/PathautoWidget.php | 6 +- .../pathauto/AliasType/ForumAliasType.php | 2 +- .../pathauto/AliasType/NodeAliasType.php | 2 +- .../AliasType/TaxonomyTermAliasType.php | 2 +- .../pathauto/AliasType/UserAliasType.php | 6 +- src/Tests/PathautoLocaleTest.php | 14 ++-- src/Tests/PathautoNodeWebTest.php | 13 ++-- src/Tests/PathautoTaxonomyWebTest.php | 4 +- src/Tests/PathautoTestHelperTrait.php | 10 +-- src/Tests/PathautoTokenTest.php | 6 +- src/Tests/PathautoUnitTest.php | 74 +++++++++---------- src/Tests/PathautoUserWebTest.php | 4 +- 15 files changed, 78 insertions(+), 77 deletions(-) diff --git a/config/install/pathauto.pattern.yml b/config/install/pathauto.pattern.yml index 008417f..cde1de5 100644 --- a/config/install/pathauto.pattern.yml +++ b/config/install/pathauto.pattern.yml @@ -1,12 +1,12 @@ patterns: node: - default: 'content/[node:title]' + default: '/content/[node:title]' taxonomy_term: - default: '[term:vocabulary]/[term:name]' + default: '/[term:vocabulary]/[term:name]' forum: - default: '[term:vocabulary]/[term:name]' + default: '/[term:vocabulary]/[term:name]' user: - default: 'users/[user:name]' + default: '/users/[user:name]' diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index b1ab5cf..dda2536 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -89,7 +89,7 @@ public function getCleanSeparators($string, $separator = NULL) { if (strlen($separator)) { // Trim any leading or trailing separators. - $output = trim($output, $separator); + $output = rtrim($output, $separator); // Escape the separator for use in regular expressions. $seppattern = preg_quote($separator, '/'); diff --git a/src/PathautoManager.php b/src/PathautoManager.php index c5e2c85..4754fb2 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -491,7 +491,7 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } $result = $this->createAlias( - $type, $op, $entity->urlInfo()->getInternalPath(), array($type => $entity), $bundle, $options['language']); + $type, $op, '/' . $entity->urlInfo()->getInternalPath(), array($type => $entity), $bundle, $options['language']); if ($type == 'taxonomy_term' && empty($options['is_child'])) { // For all children generate new aliases. diff --git a/src/Plugin/Field/FieldWidget/PathautoWidget.php b/src/Plugin/Field/FieldWidget/PathautoWidget.php index 87ec748..4a1a967 100644 --- a/src/Plugin/Field/FieldWidget/PathautoWidget.php +++ b/src/Plugin/Field/FieldWidget/PathautoWidget.php @@ -61,9 +61,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if (!isset($entity->path->pathauto)) { if (!$entity->isNew()) { module_load_include('inc', 'pathauto'); - $path = \Drupal::service('path.alias_manager')->getAliasByPath($entity->urlInfo()->getInternalPath(), $entity->language()->getId()); - $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', $entity->urlInfo()->getInternalPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); - $entity->path->pathauto = ($path != $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); + $path = \Drupal::service('path.alias_manager')->getAliasByPath('/' . $entity->urlInfo()->getInternalPath(), $entity->language()->getId()); + $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', '/' . $entity->urlInfo()->getInternalPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); + $entity->path->pathauto = ($path != '/' . $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); } else { $entity->path->pathauto = TRUE; diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 1cd291c..155bf2b 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -40,7 +40,7 @@ public function getPatterns() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('default' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + return array('default' => array('/[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 5b701b3..86601e2 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -33,7 +33,7 @@ public function getPatternDescription() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('default' => array('content/[node:title]')) + parent::defaultConfiguration(); + return array('default' => array('/content/[node:title]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 2056787..4a0c24d 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -33,7 +33,7 @@ public function getPatternDescription() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('default' => array('[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); + return array('default' => array('/[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } /** diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index e918e0d..0be986a 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -33,7 +33,7 @@ public function getPatternDescription() { * {@inheritdoc} */ public function defaultConfiguration() { - return array('default' => array('users/[user:name]')) + parent::defaultConfiguration(); + return array('default' => array('/users/[user:name]')) + parent::defaultConfiguration(); } /** @@ -46,7 +46,7 @@ public function batchUpdate(&$context) { } $query = db_select('users', 'u'); - $query->leftJoin('url_alias', 'ua', "CONCAT('user/', u.uid) = ua.source"); + $query->leftJoin('url_alias', 'ua', "CONCAT('/user/', u.uid) = ua.source"); $query->addField('u', 'uid'); $query->isNull('ua.source'); $query->condition('u.uid', $context['sandbox']['current'], '>'); @@ -84,7 +84,7 @@ public function batchUpdate(&$context) { * {@inheritdoc} */ public function getSourcePrefix() { - return 'user/'; + return '/user/'; } } diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 187a18b..bfb0258 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -47,30 +47,30 @@ function testLanguageAliases() { 'title' => 'English node', 'langcode' => 'en', 'path' => array(array( - 'alias' => 'english-node', + 'alias' => '/english-node', 'pathauto' => FALSE, )), ); $node = $this->drupalCreateNode($node); - $english_alias = \Drupal::service('path.alias_storage')->load(array('alias' => 'english-node', 'langcode' => 'en')); + $english_alias = \Drupal::service('path.alias_storage')->load(array('alias' => '/english-node', 'langcode' => 'en')); $this->assertTrue($english_alias, 'Alias created with proper language.'); // Also save a French alias that should not be left alone, even though // it is the newer alias. - $this->saveEntityAlias($node, 'french-node', 'fr'); + $this->saveEntityAlias($node, '/french-node', 'fr'); // Add an alias with the soon-to-be generated alias, causing the upcoming // alias update to generate a unique alias with the '-0' suffix. - $this->saveAlias('node/invalid', 'content/english-node', Language::LANGCODE_NOT_SPECIFIED); + $this->saveAlias('/node/invalid', '/content/english-node', Language::LANGCODE_NOT_SPECIFIED); // Update the node, triggering a change in the English alias. $node->path->pathauto = TRUE; $node->save(); // Check that the new English alias replaced the old one. - $this->assertEntityAlias($node, 'content/english-node-0', 'en'); - $this->assertEntityAlias($node, 'french-node', 'fr'); - $this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => 'content/english-node-0')); + $this->assertEntityAlias($node, '/content/english-node-0', 'en'); + $this->assertEntityAlias($node, '/french-node', 'fr'); + $this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => '/content/english-node-0')); } } diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index 1f1d37d..98ef7c2 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -63,7 +63,7 @@ function testNodeEditing() { // Create a node by saving the node form. $title = ' Testing: node title ['; - $automatic_alias = 'content/testing-node-title'; + $automatic_alias = '/content/testing-node-title'; $this->drupalPostForm(NULL, array('title[0][value]' => $title), t('Save and publish')); $node = $this->drupalGetNodeByTitle($title); @@ -77,7 +77,7 @@ function testNodeEditing() { $this->assertText($title, 'Node accessible through automatic alias.'); // Manually set the node's alias. - $manual_alias = 'content/' . $node->id(); + $manual_alias = '/content/' . $node->id(); $edit = array( 'path[0][pathauto]' => FALSE, 'path[0][alias]' => $manual_alias, @@ -107,12 +107,13 @@ function testNodeEditing() { $edit = array( 'title[0][value]' => $title, 'path[0][pathauto]' => TRUE, - 'path[0][alias]' => 'should-not-get-created', + 'path[0][alias]' => '/should-not-get-created', ); $this->drupalPostForm('node/add/page', $edit, t('Save and publish')); $this->assertNoAliasExists(array('alias' => 'should-not-get-created')); $node = $this->drupalGetNodeByTitle($title); - $this->assertEntityAlias($node, 'content/automatic-title'); + debug($node); + $this->assertEntityAlias($node, '/content/automatic-title'); // Remove the pattern for nodes, the pathauto checkbox should not be // displayed. @@ -158,8 +159,8 @@ function testNodeOperations() { '%action' => 'Update URL-Alias', ))); - $this->assertEntityAlias($node1, 'content/' . $node1->getTitle()); - $this->assertEntityAlias($node2, 'node/' . $node2->id()); + $this->assertEntityAlias($node1, '/content/' . $node1->getTitle()); + $this->assertEntityAlias($node2, '/node/' . $node2->id()); } } diff --git a/src/Tests/PathautoTaxonomyWebTest.php b/src/Tests/PathautoTaxonomyWebTest.php index 46828a6..c1640c1 100644 --- a/src/Tests/PathautoTaxonomyWebTest.php +++ b/src/Tests/PathautoTaxonomyWebTest.php @@ -61,7 +61,7 @@ function testTermEditing() { // Create term for testing. $name = 'Testing: term name ['; - $automatic_alias = 'tags/testing-term-name'; + $automatic_alias = '/tags/testing-term-name'; $this->drupalPostForm('admin/structure/taxonomy/manage/tags/add', array('name[0][value]' => $name), 'Save'); $name = trim($name); $this->assertText("Created new term $name."); @@ -77,7 +77,7 @@ function testTermEditing() { $this->assertText($name, 'Term accessible through automatic alias.'); // Manually set the term's alias. - $manual_alias = 'tags/' . $term->id(); + $manual_alias = '/tags/' . $term->id(); $edit = array( 'path[0][pathauto]' => FALSE, 'path[0][alias]' => $manual_alias, diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 8e40265..7aee60f 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -33,7 +33,7 @@ public function saveEntityAlias(EntityInterface $entity, $alias, $langcode = NUL if (!$langcode) { $langcode = $entity->language()->getId(); } - return $this->saveAlias($entity->urlInfo()->getInternalPath(), $alias, $langcode); + return $this->saveAlias('/' . $entity->urlInfo()->getInternalPath(), $alias, $langcode); } public function assertEntityAlias(EntityInterface $entity, $expected_alias, $langcode = NULL) { @@ -41,11 +41,11 @@ public function assertEntityAlias(EntityInterface $entity, $expected_alias, $lan if (!$langcode) { $langcode = $entity->language()->getId(); } - $this->assertAlias($entity->urlInfo()->getInternalPath(), $expected_alias, $langcode); + $this->assertAlias('/' . $entity->urlInfo()->getInternalPath(), $expected_alias, $langcode); } public function assertEntityAliasExists(EntityInterface $entity) { - return $this->assertAliasExists(array('source' => $entity->urlInfo()->getInternalPath())); + return $this->assertAliasExists(array('source' => '/' . $entity->urlInfo()->getInternalPath())); } public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) { @@ -53,11 +53,11 @@ public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) { if (!$langcode) { $langcode = $entity->language()->getId(); } - $this->assertEntityAlias($entity, $entity->urlInfo()->getInternalPath(), $langcode); + $this->assertEntityAlias($entity, '/' . $entity->urlInfo()->getInternalPath(), $langcode); } public function assertNoEntityAliasExists(EntityInterface $entity) { - $this->assertNoAliasExists(array('source' => $entity->urlInfo()->getInternalPath())); + $this->assertNoAliasExists(array('source' => '/' . $entity->urlInfo()->getInternalPath())); } public function assertAlias($source, $expected_alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) { diff --git a/src/Tests/PathautoTokenTest.php b/src/Tests/PathautoTokenTest.php index fbca356..117bf68 100644 --- a/src/Tests/PathautoTokenTest.php +++ b/src/Tests/PathautoTokenTest.php @@ -29,11 +29,11 @@ public function testPathautoTokens() { $array = array( 'test first arg', - 'The Array / value', + 'the Array / value', ); $tokens = array( - 'join-path' => 'test-first-arg/array-value', + 'join' => 'test-first-arg/array-value', ); $data['array'] = $array; $replacements = $this->assertTokens('array', $data, $tokens); @@ -42,7 +42,7 @@ public function testPathautoTokens() { /* @var \Drupal\pathauto\PathautoManagerInterface $manager */ $manager = \Drupal::service('pathauto.manager'); $manager->cleanTokenValues($replacements, $data, array()); - $this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value'); + $this->assertEqual($replacements['[array:join]'], 'test-first-arg/array-value'); } /** diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index e816201..7dbed7c 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -55,9 +55,9 @@ public function testGetSchemaAliasMaxLength() { */ public function testPatternLoadByEntity() { $this->config('pathauto.pattern') - ->set('patterns.node.bundles.article.default', 'article/[node:title]') - ->set('patterns.node.bundles.article.languages.en', 'article/en/[node:title]') - ->set('patterns.node.bundles.page.default', '[node:title]') + ->set('patterns.node.bundles.article.default', '/article/[node:title]') + ->set('patterns.node.bundles.article.languages.en', '/article/en/[node:title]') + ->set('patterns.node.bundles.page.default', '/[node:title]') ->save(); $tests = array( @@ -65,31 +65,31 @@ public function testPatternLoadByEntity() { 'entity' => 'node', 'bundle' => 'article', 'language' => 'fr', - 'expected' => 'article/[node:title]', + 'expected' => '/article/[node:title]', ), array( 'entity' => 'node', 'bundle' => 'article', 'language' => 'en', - 'expected' => 'article/en/[node:title]', + 'expected' => '/article/en/[node:title]', ), array( 'entity' => 'node', 'bundle' => 'article', 'language' => Language::LANGCODE_NOT_SPECIFIED, - 'expected' => 'article/[node:title]', + 'expected' => '/article/[node:title]', ), array( 'entity' => 'node', 'bundle' => 'page', 'language' => 'en', - 'expected' => '[node:title]', + 'expected' => '/[node:title]', ), array( 'entity' => 'user', 'bundle' => 'user', 'language' => Language::LANGCODE_NOT_SPECIFIED, - 'expected' => 'users/[user:name]', + 'expected' => '/users/[user:name]', ), array( 'entity' => 'invalid-entity', @@ -155,7 +155,7 @@ public function testCleanString() { public function testCleanAlias() { $tests = array(); $tests['one/two/three'] = 'one/two/three'; - $tests['/one/two/three/'] = 'one/two/three'; + $tests['/one/two/three/'] = '/one/two/three'; $tests['one//two///three'] = 'one/two/three'; $tests['one/two--three/-/--/-/--/four---five'] = 'one/two-three/four-five'; $tests['one/-//three--/four'] = 'one/three/four'; @@ -174,16 +174,16 @@ public function testCleanAlias() { * Test pathauto_path_delete_multiple(). */ public function testPathDeleteMultiple() { - $this->saveAlias('node/1', 'node-1-alias'); - $this->saveAlias('node/1/view', 'node-1-alias/view'); - $this->saveAlias('node/1', 'node-1-alias-en', 'en'); - $this->saveAlias('node/1', 'node-1-alias-fr', 'fr'); - $this->saveAlias('node/2', 'node-2-alias'); - - \Drupal::service('pathauto.alias_storage_helper')->deleteAll('node/1'); - $this->assertNoAliasExists(array('source' => "node/1")); - $this->assertNoAliasExists(array('source' => "node/1/view")); - $this->assertAliasExists(array('source' => "node/2")); + $this->saveAlias('/node/1', '/node-1-alias'); + $this->saveAlias('/node/1/view', '/node-1-alias/view'); + $this->saveAlias('/node/1', '/node-1-alias-en', 'en'); + $this->saveAlias('/node/1', '/node-1-alias-fr', 'fr'); + $this->saveAlias('/node/2', '/node-2-alias'); + + \Drupal::service('pathauto.alias_storage_helper')->deleteAll('/node/1'); + $this->assertNoAliasExists(array('source' => "/node/1")); + $this->assertNoAliasExists(array('source' => "/node/1/view")); + $this->assertAliasExists(array('source' => "/node/2")); } /** @@ -196,7 +196,7 @@ public function testUpdateActions() { $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); $config->save(); $node = $this->drupalCreateNode(array('title' => 'First title')); - $this->assertEntityAlias($node, 'content/first-title'); + $this->assertEntityAlias($node, '/content/first-title'); $node->path->pathauto = TRUE; @@ -205,16 +205,16 @@ public function testUpdateActions() { $config->save(); $node->setTitle('Second title'); $node->save(); - $this->assertEntityAlias($node, 'content/second-title'); - $this->assertNoAliasExists(array('alias' => 'content/first-title')); + $this->assertEntityAlias($node, '/content/second-title'); + $this->assertNoAliasExists(array('alias' => '/content/first-title')); // Test PATHAUTO_UPDATE_ACTION_LEAVE $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_LEAVE); $config->save(); $node->setTitle('Third title'); $node->save(); - $this->assertEntityAlias($node, 'content/third-title'); - $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); + $this->assertEntityAlias($node, '/content/third-title'); + $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); $config->save(); @@ -223,26 +223,26 @@ public function testUpdateActions() { $this->assertEntityAlias($node, 'content/fourth-title'); $this->assertNoAliasExists(array('alias' => 'content/third-title')); // The older second alias is not deleted yet. - $older_path = $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); + $older_path = $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); \Drupal::service('path.alias_storage')->delete($older_path); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); $config->save(); $node->setTitle('Fifth title'); $node->save(); - $this->assertEntityAlias($node, 'content/fourth-title'); - $this->assertNoAliasExists(array('alias' => 'content/fifth-title')); + $this->assertEntityAlias($node, '/content/fourth-title'); + $this->assertNoAliasExists(array('alias' => '/content/fifth-title')); // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'update'. $this->deleteAllAliases(); $node->save(); - $this->assertEntityAlias($node, 'content/fifth-title'); + $this->assertEntityAlias($node, '/content/fifth-title'); // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'. $this->deleteAllAliases(); $node->setTitle('Sixth title'); \Drupal::service('pathauto.manager')->updateAlias($node, 'bulkupdate'); - $this->assertEntityAlias($node, 'content/sixth-title'); + $this->assertEntityAlias($node, '/content/sixth-title'); } /** @@ -255,7 +255,7 @@ public function testNoTokensNoAlias() { $node->setTitle('hello'); $node->save(); - $this->assertEntityAlias($node, 'content/hello'); + $this->assertEntityAlias($node, '/content/hello'); } /** @@ -269,14 +269,14 @@ public function testPathTokens() { $vocab = $this->addVocabulary(); $term1 = $this->addTerm($vocab, array('name' => 'Parent term')); - $this->assertEntityAlias($term1, 'parent-term'); + $this->assertEntityAlias($term1, '/parent-term'); $term2 = $this->addTerm($vocab, array('name' => 'Child term', 'parent' => $term1->id())); - $this->assertEntityAlias($term2, 'parent-term/child-term'); + $this->assertEntityAlias($term2, '/parent-term/child-term'); - $this->saveEntityAlias($term1, 'My Crazy/Alias/'); + $this->saveEntityAlias($term1, '/My Crazy/Alias/'); $term2->save(); - $this->assertEntityAlias($term2, 'My Crazy/Alias/child-term'); + $this->assertEntityAlias($term2, '/My Crazy/Alias/child-term'); } public function testEntityBundleRenamingDeleting() { @@ -339,11 +339,11 @@ function testNoExistingPathAliases() { */ function testProgrammaticEntityCreation() { $node = $this->drupalCreateNode(array('title' => 'Test node', 'path' => array('pathauto' => TRUE))); - $this->assertEntityAlias($node, 'content/test-node'); + $this->assertEntityAlias($node, '/content/test-node'); $vocabulary = $this->addVocabulary(array('name' => 'Tags')); $term = $this->addTerm($vocabulary, array('name' => 'Test term', 'path' => array('pathauto' => TRUE))); - $this->assertEntityAlias($term, 'tags/test-term'); + $this->assertEntityAlias($term, '/tags/test-term'); $edit['name'] = 'Test user'; $edit['mail'] = 'test-user@example.com'; @@ -352,7 +352,7 @@ function testProgrammaticEntityCreation() { $edit['status'] = 1; $account = entity_create('user', $edit); $account->save(); - $this->assertEntityAlias($account, 'users/test-user'); + $this->assertEntityAlias($account, '/users/test-user'); } protected function drupalCreateNode(array $settings = array()) { diff --git a/src/Tests/PathautoUserWebTest.php b/src/Tests/PathautoUserWebTest.php index 3b6f898..bef28f4 100644 --- a/src/Tests/PathautoUserWebTest.php +++ b/src/Tests/PathautoUserWebTest.php @@ -90,8 +90,8 @@ function testUserOperations() { '%action' => 'Update URL-Alias', ))); - $this->assertEntityAlias($account, 'users/' . Unicode::strtolower($account->getUsername())); - $this->assertEntityAlias($this->adminUser, 'user/' . $this->adminUser->id()); + $this->assertEntityAlias($account, '/users/' . Unicode::strtolower($account->getUsername())); + $this->assertEntityAlias($this->adminUser, '/user/' . $this->adminUser->id()); } } From 25b77a0bb76c5ba58b790ff9201598ef3fd7fe23 Mon Sep 17 00:00:00 2001 From: Lukas Schneider Date: Fri, 10 Jul 2015 12:40:10 +0200 Subject: [PATCH 031/169] removed left over debug() --- src/Tests/PathautoNodeWebTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index 98ef7c2..eb68e9e 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -112,7 +112,6 @@ function testNodeEditing() { $this->drupalPostForm('node/add/page', $edit, t('Save and publish')); $this->assertNoAliasExists(array('alias' => 'should-not-get-created')); $node = $this->drupalGetNodeByTitle($title); - debug($node); $this->assertEntityAlias($node, '/content/automatic-title'); // Remove the pattern for nodes, the pathauto checkbox should not be From aa8b0c0cad0ee5c735e84ba26dce8fd3efd7d5c3 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 12:51:40 +0200 Subject: [PATCH 032/169] Fixed token and node alias test --- src/AliasCleaner.php | 7 ++++++- src/Tests/AliasType/NodeAliasTest.php | 2 +- src/Tests/PathautoTokenTest.php | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index dda2536..565f31d 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -89,7 +89,7 @@ public function getCleanSeparators($string, $separator = NULL) { if (strlen($separator)) { // Trim any leading or trailing separators. - $output = rtrim($output, $separator); + $output = trim($output, $separator); // Escape the separator for use in regular expressions. $seppattern = preg_quote($separator, '/'); @@ -101,6 +101,11 @@ public function getCleanSeparators($string, $separator = NULL) { if ($separator !== '/') { $output = preg_replace("/\/+$seppattern\/+|$seppattern\/+|\/+$seppattern/", "/", $output); } + else { + // If the separator is a slash, we need to re-add the leading slash + // dropped by the trim function. + $output = '/' . $output; + } } return $output; diff --git a/src/Tests/AliasType/NodeAliasTest.php b/src/Tests/AliasType/NodeAliasTest.php index d745482..c5bfede 100644 --- a/src/Tests/AliasType/NodeAliasTest.php +++ b/src/Tests/AliasType/NodeAliasTest.php @@ -46,7 +46,7 @@ public function testNodeAlias() { $default_config = $node_type->defaultConfiguration(); $this->assertTrue(array_key_exists('default', $default_config), "Default key exists."); - $this->assertEqual($default_config['default'][0], 'content/[node:title]', "Default content pattern matches."); + $this->assertEqual($default_config['default'][0], '/content/[node:title]', "Default content pattern matches."); } diff --git a/src/Tests/PathautoTokenTest.php b/src/Tests/PathautoTokenTest.php index 117bf68..516bcb5 100644 --- a/src/Tests/PathautoTokenTest.php +++ b/src/Tests/PathautoTokenTest.php @@ -33,7 +33,7 @@ public function testPathautoTokens() { ); $tokens = array( - 'join' => 'test-first-arg/array-value', + 'join-path' => 'test-first-arg/array-value', ); $data['array'] = $array; $replacements = $this->assertTokens('array', $data, $tokens); @@ -42,7 +42,7 @@ public function testPathautoTokens() { /* @var \Drupal\pathauto\PathautoManagerInterface $manager */ $manager = \Drupal::service('pathauto.manager'); $manager->cleanTokenValues($replacements, $data, array()); - $this->assertEqual($replacements['[array:join]'], 'test-first-arg/array-value'); + $this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value'); } /** From 17ed53defd3e95793cc210fb6a07a600c3765748 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 13:10:19 +0200 Subject: [PATCH 033/169] Fixing unit tests --- src/AliasUniquifier.php | 2 +- src/Tests/PathautoTokenTest.php | 2 +- src/Tests/PathautoUnitTest.php | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 09eaac7..10554a1 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -143,7 +143,7 @@ public function isRoute($path) { } try { - $this->urlMatcher->match('/' . $path); + $this->urlMatcher->match($path); return TRUE; } catch (ResourceNotFoundException $e) { diff --git a/src/Tests/PathautoTokenTest.php b/src/Tests/PathautoTokenTest.php index 516bcb5..fbca356 100644 --- a/src/Tests/PathautoTokenTest.php +++ b/src/Tests/PathautoTokenTest.php @@ -29,7 +29,7 @@ public function testPathautoTokens() { $array = array( 'test first arg', - 'the Array / value', + 'The Array / value', ); $tokens = array( diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 7dbed7c..dd996aa 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -154,11 +154,11 @@ public function testCleanString() { */ public function testCleanAlias() { $tests = array(); - $tests['one/two/three'] = 'one/two/three'; + $tests['one/two/three'] = '/one/two/three'; $tests['/one/two/three/'] = '/one/two/three'; - $tests['one//two///three'] = 'one/two/three'; - $tests['one/two--three/-/--/-/--/four---five'] = 'one/two-three/four-five'; - $tests['one/-//three--/four'] = 'one/three/four'; + $tests['one//two///three'] = '/one/two/three'; + $tests['one/two--three/-/--/-/--/four---five'] = '/one/two-three/four-five'; + $tests['one/-//three--/four'] = '/one/three/four'; foreach ($tests as $input => $expected) { $output = \Drupal::service('pathauto.alias_cleaner')->cleanAlias($input); @@ -214,16 +214,16 @@ public function testUpdateActions() { $node->setTitle('Third title'); $node->save(); $this->assertEntityAlias($node, '/content/third-title'); - $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => 'content/second-title')); + $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); $config->save(); $node->setTitle('Fourth title'); $node->save(); - $this->assertEntityAlias($node, 'content/fourth-title'); - $this->assertNoAliasExists(array('alias' => 'content/third-title')); + $this->assertEntityAlias($node, '/content/fourth-title'); + $this->assertNoAliasExists(array('alias' => '/content/third-title')); // The older second alias is not deleted yet. - $older_path = $this->assertAliasExists(array('source' => $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); + $older_path = $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); \Drupal::service('path.alias_storage')->delete($older_path); $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); @@ -263,7 +263,7 @@ public function testNoTokensNoAlias() { */ public function testPathTokens() { $config = $this->config('pathauto.pattern'); - $config->set('patterns.taxonomy_term.default', '[term:parent:url:path]/[term:name]'); + $config->set('patterns.taxonomy_term.default', '/[term:parent:url:path]/[term:name]'); $config->save(); $vocab = $this->addVocabulary(); @@ -315,23 +315,23 @@ function testNoExistingPathAliases() { // Check that Pathauto does not create an alias of '/admin'. $node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page')); - $this->assertEntityAlias($node, 'admin-0'); + $this->assertEntityAlias($node, '/admin-0'); // Check that Pathauto does not create an alias of '/modules'. $node->setTitle('Modules'); $node->save(); - $this->assertEntityAlias($node, 'modules-0'); + $this->assertEntityAlias($node, '/modules-0'); // Check that Pathauto does not create an alias of '/index.php'. $node->setTitle('index.php'); $node->save(); - $this->assertEntityAlias($node, 'index.php-0'); + $this->assertEntityAlias($node, '/index.php-0'); // Check that a safe value gets an automatic alias. This is also a control // to ensure the above tests work properly. $node->setTitle('Safe value'); $node->save(); - $this->assertEntityAlias($node, 'safe-value'); + $this->assertEntityAlias($node, '/safe-value'); } /** From fa34bdc6887a447575437ed92270f18580b0c368 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 13:17:52 +0200 Subject: [PATCH 034/169] Fix .travis.yml version number --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 78b774f..8b1a059 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,8 @@ cache: apt: true php: - - 5.4 - 5.5 + - 5.6 env: - PATH=$PATH:/home/travis/.composer/vendor/bin From 60a3add6cda98c82573a433553d5339ff1aea480 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 13:54:15 +0200 Subject: [PATCH 035/169] Use alias storage, explicitly handle an alias that points to the given source --- pathauto.services.yml | 2 +- src/AliasUniquifier.php | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pathauto.services.yml b/pathauto.services.yml index 5e244db..fbccd04 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -10,7 +10,7 @@ services: arguments: ['@config.factory', '@path.alias_storage', '@database','@pathauto.verbose_messenger', '@string_translation'] pathauto.alias_uniquifier: class: Drupal\pathauto\AliasUniquifier - arguments: ['@config.factory', '@pathauto.alias_storage_helper','@module_handler', '@router.no_access_checks'] + arguments: ['@config.factory', '@module_handler', '@router.no_access_checks', '@path.alias_manager'] pathauto.verbose_messenger: class: Drupal\pathauto\VerboseMessenger arguments: ['@config.factory', '@current_user'] diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 10554a1..31c9d01 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -11,6 +11,8 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\Path\AliasManagerInterface; +use Drupal\Core\Path\AliasStorageInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; @@ -26,13 +28,6 @@ class AliasUniquifier implements AliasUniquifierInterface { */ protected $configFactory; - /** - * The alias storage helper. - * - * @var \Drupal\pathauto\AliasStorageHelperInterface - */ - protected $aliasStorageHelper; - /** * The module handler. * @@ -47,23 +42,28 @@ class AliasUniquifier implements AliasUniquifierInterface { */ protected $urlMatcher; + /** + * The alias manager. + * + * @var \Drupal\Core\Path\AliasManagerInterface + */ + protected $aliasManager; + /** * Creates a new AliasUniquifier. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. - * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper - * The alias storage helper. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $url_matcher * The url matcher service. */ - public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, ModuleHandlerInterface $module_handler, UrlMatcherInterface $url_matcher) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, UrlMatcherInterface $url_matcher, AliasManagerInterface $alias_manager) { $this->configFactory = $config_factory; - $this->aliasStorageHelper = $alias_storage_helper; $this->moduleHandler = $module_handler; $this->urlMatcher = $url_matcher; + $this->aliasManager = $alias_manager; } /** @@ -94,10 +94,13 @@ public function uniquify(&$alias, $source, $langcode) { * {@inheritdoc} */ public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) { - // First check whether the alias exists for another source. - if ($this->aliasStorageHelper->exists($alias, $source, $langcode)) { - return TRUE; + // Check if this alias already exists. + if ($existing_source = $this->aliasManager->getPathByAlias($alias, $langcode)) { + // If it is an alias for the provided source, it is allowed to keep using + // it. If not, then it is reserved. + return $existing_source != $source; } + // Then check if there is a route with the same path. if ($this->isRoute($alias)) { return TRUE; @@ -126,8 +129,6 @@ public function isReserved($alias, $source, $langcode = LanguageInterface::LANGC /** * Verify if the given path is a valid route. * - * Taken from menu_execute_active_handler(). - * * @param string $path * A string containing a relative path. * From edd15511bf628b363496ce1f2b7e2510cb270d62 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 14:00:25 +0200 Subject: [PATCH 036/169] keep the helper --- src/AliasUniquifier.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 31c9d01..2c46a18 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -28,6 +28,13 @@ class AliasUniquifier implements AliasUniquifierInterface { */ protected $configFactory; + /** + * The alias storage helper. + * + * @var \Drupal\pathauto\AliasStorageHelperInterface + */ + protected $aliasStorageHelper; + /** * The module handler. * @@ -54,13 +61,16 @@ class AliasUniquifier implements AliasUniquifierInterface { * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. + * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper + * The alias storage helper. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $url_matcher * The url matcher service. */ - public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, UrlMatcherInterface $url_matcher, AliasManagerInterface $alias_manager) { + public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, ModuleHandlerInterface $module_handler, UrlMatcherInterface $url_matcher, AliasManagerInterface $alias_manager) { $this->configFactory = $config_factory; + $this->aliasStorageHelper = $alias_storage_helper; $this->moduleHandler = $module_handler; $this->urlMatcher = $url_matcher; $this->aliasManager = $alias_manager; From 042b12d42bceb54f4a67bff12b0655ce5ef189c1 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 14:07:19 +0200 Subject: [PATCH 037/169] Add storage helper back in services.yml --- pathauto.services.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.services.yml b/pathauto.services.yml index fbccd04..c740b6b 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -10,7 +10,7 @@ services: arguments: ['@config.factory', '@path.alias_storage', '@database','@pathauto.verbose_messenger', '@string_translation'] pathauto.alias_uniquifier: class: Drupal\pathauto\AliasUniquifier - arguments: ['@config.factory', '@module_handler', '@router.no_access_checks', '@path.alias_manager'] + arguments: ['@config.factory', '@pathauto.alias_storage_helper','@module_handler', '@router.no_access_checks', '@path.alias_manager'] pathauto.verbose_messenger: class: Drupal\pathauto\VerboseMessenger arguments: ['@config.factory', '@current_user'] From 627244c084b891cbb09f8f5c08923b4b2e797997 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 11 Jul 2015 14:20:54 +0200 Subject: [PATCH 038/169] alias manager returns the alias if there is alias stored --- src/AliasUniquifier.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 2c46a18..16285c9 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -106,9 +106,12 @@ public function uniquify(&$alias, $source, $langcode) { public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) { // Check if this alias already exists. if ($existing_source = $this->aliasManager->getPathByAlias($alias, $langcode)) { - // If it is an alias for the provided source, it is allowed to keep using - // it. If not, then it is reserved. - return $existing_source != $source; + if ($existing_source != $alias) { + // If it is an alias for the provided source, it is allowed to keep using + // it. If not, then it is reserved. + return $existing_source != $source; + } + } // Then check if there is a route with the same path. From ae3cefc5c15118dfb40de48b800230e6e22ae4d8 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 13 Jul 2015 23:12:47 +0200 Subject: [PATCH 039/169] Use a temporary feature branch for token to make the tests pass --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8b1a059..d447ec4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ install: - cd $TRAVIS_BUILD_DIR/.. - git clone --depth 1 --branch 8.0.x http://git.drupal.org/project/drupal.git - cd drupal/modules - - git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git + - git clone --depth 1 --branch 2516454 https://github.com/md-systems/token.git # Restart apache and test it - sudo service apache2 restart - curl -v "http://localhost" From 45f9b5bb0a7d0e51002f8a42a9651dacf7c006be Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:25:40 +0200 Subject: [PATCH 040/169] Add before script --- .travis-before-script.sh | 12 +++ .travis.yml | 166 +++++++++++++++++++++------------------ 2 files changed, 101 insertions(+), 77 deletions(-) create mode 100644 .travis-before-script.sh diff --git a/.travis-before-script.sh b/.travis-before-script.sh new file mode 100644 index 0000000..8689f31 --- /dev/null +++ b/.travis-before-script.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e $DRUPAL_TI_DEBUG + +# Ensure the right Drupal version is installed. +# Note: This function is re-entrant. +drupal_ti_ensure_drupal + +# Download token 8.x-1.x +cd "$DRUPAL_TI_DRUPAL_DIR" +cd modules +git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git diff --git a/.travis.yml b/.travis.yml index d447ec4..fa0e823 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,96 +1,108 @@ +# @file +# .travis.yml - Drupal for Travis CI Integration +# +# Template provided by https://github.com/LionsAd/drupal_ti. +# +# Based for simpletest upon: +# https://github.com/sonnym/travis-ci-drupal-module-example + language: php -cache: - bundler: true - directories: - - $HOME/tmp/drush - - $HOME/.bundle - apt: true + +# Comment out this line if you only use unit tests. +sudo: required php: - 5.5 - 5.6 + - 7 + - hhvm + +matrix: + fast_finish: true + allow_failures: + - php: 7 + - php: hhvm env: - - PATH=$PATH:/home/travis/.composer/vendor/bin + global: + # add composer's global bin directory to the path + # see: https://github.com/drush-ops/drush#install---composer + - PATH="$PATH:$HOME/.composer/vendor/bin" + + # Configuration variables. + - DRUPAL_TI_MODULE_NAME="pathauto" + - DRUPAL_TI_SIMPLETEST_GROUP="pathauto" + + # Define runners and environment vars to include before and after the + # main runners / environment vars. + #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./drupal_ti/before" + #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" + + # The environment to use, supported are: drupal-7, drupal-8 + - DRUPAL_TI_ENVIRONMENT="drupal-8" + + # Drupal specific variables. + - DRUPAL_TI_DB="drupal_travis_db" + - DRUPAL_TI_DB_URL="mysql://root:@127.0.0.1/drupal_travis_db" + # Note: Do not add a trailing slash here. + - DRUPAL_TI_WEBSERVER_URL="http://127.0.0.1" + - DRUPAL_TI_WEBSERVER_PORT="8080" + + # Simpletest specific commandline arguments, the DRUPAL_TI_SIMPLETEST_GROUP is appended at the end. + - DRUPAL_TI_SIMPLETEST_ARGS="--verbose --color --concurrency 4 --url $DRUPAL_TI_WEBSERVER_URL:$DRUPAL_TI_WEBSERVER_PORT" + + # === Behat specific variables. + # This is relative to $TRAVIS_BUILD_DIR + - DRUPAL_TI_BEHAT_DIR="./tests/behat" + # These arguments are passed to the bin/behat command. + - DRUPAL_TI_BEHAT_ARGS="" + # Specify the filename of the behat.yml with the $DRUPAL_TI_DRUPAL_DIR variables. + - DRUPAL_TI_BEHAT_YML="behat.yml.dist" + # This is used to setup Xvfb. + - DRUPAL_TI_BEHAT_SCREENSIZE_COLOR="1280x1024x16" + # The version of seleniumthat should be used. + - DRUPAL_TI_BEHAT_SELENIUM_VERSION="2.44" + + # PHPUnit specific commandline arguments. + - DRUPAL_TI_PHPUNIT_ARGS="" + + # Code coverage via coveralls.io + - DRUPAL_TI_COVERAGE="satooshi/php-coveralls:0.6.*" + # This needs to match your .coveralls.yml file. + - DRUPAL_TI_COVERAGE_FILE="build/logs/clover.xml" + + # Debug options + #- DRUPAL_TI_DEBUG="-x -v" + # Set to "all" to output all files, set to e.g. "xvfb selenium" or "selenium", + # etc. to only output those channels. + #- DRUPAL_TI_DEBUG_FILE_OUTPUT="selenium xvfb webserver" + + matrix: + # [[[ SELECT ANY OR MORE OPTIONS ]]] + #- DRUPAL_TI_RUNNERS="phpunit" + #- DRUPAL_TI_RUNNERS="simpletest" + #- DRUPAL_TI_RUNNERS="behat" + - DRUPAL_TI_RUNNERS=" simpletest" -# This will create the database mysql: - database: drupal + database: drupal_travis_db username: root encoding: utf8 -# To be able to run a webbrowser -# If we need anything more powerful -# than e.g. phantomjs before_install: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - composer self-update + - composer global require "lionsad/drupal_ti:1.*" + - drupal-ti before_install install: - # Grab Drush - - composer global require drush/drush:dev-master --prefer-source - - cd /home/travis/.composer/vendor/drush/drush && cd - - # Make sure we don't fail when checking out projects - - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - # LAMP package installation (mysql is already started) - - sudo apt-get update - - sudo apt-get install apache2 libapache2-mod-fastcgi - # enable php-fpm, travis does not support any other method with php and apache - - sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf - - sudo a2enmod rewrite actions fastcgi alias - - echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - ~/.phpenv/versions/$(phpenv version-name)/sbin/php-fpm - # Make sure the apache root is in our wanted directory - - echo "$(curl -fsSL https://gist.githubusercontent.com/nickveenhof/11386315/raw/b8abaf9304fe12b5cc7752d39c29c1edae8ac2e6/gistfile1.txt)" | sed -e "s,PATH,$TRAVIS_BUILD_DIR/../drupal,g" | sudo tee /etc/apache2/sites-available/default > /dev/null - # Set sendmail so drush doesn't throw an error during site install. - - echo "sendmail_path='true'" >> `php --ini | grep "Loaded Configuration" | awk '{print $4}'` - # Forward the errors to the syslog so we can print them - - echo "error_log=syslog" >> `php --ini | grep "Loaded Configuration" | awk '{print $4}'` - # Get latest drupal 8 core - - cd $TRAVIS_BUILD_DIR/.. - - git clone --depth 1 --branch 8.0.x http://git.drupal.org/project/drupal.git - - cd drupal/modules - - git clone --depth 1 --branch 2516454 https://github.com/md-systems/token.git - # Restart apache and test it - - sudo service apache2 restart - - curl -v "http://localhost" - # Re-enable when trying to get CodeSniffer doesn't return a 403 anymore. - #- composer global require drupal/coder:\>7 + - drupal-ti install before_script: - - cd $TRAVIS_BUILD_DIR/../drupal - # Update drupal core - - git pull origin 8.0.x - # Install the site - - drush -v site-install minimal --db-url=mysql://root:@localhost/drupal --yes - - drush en --yes simpletest - - drush cr - - phpenv rehash + - drupal-ti --include .travis-before-script.sh + - drupal-ti before_script script: - # go to our Drupal module directory - - mkdir $TRAVIS_BUILD_DIR/../drupal/modules/pathauto - - cp -R $TRAVIS_BUILD_DIR/* $TRAVIS_BUILD_DIR/../drupal/modules/pathauto/ - # go to our Drupal main directory - - cd $TRAVIS_BUILD_DIR/../drupal - - ls -la $TRAVIS_BUILD_DIR/../drupal/sites/default - # Run the tests - - php core/scripts/run-tests.sh --verbose --color --concurrency 4 --php `which php` --url http://localhost "pathauto" | tee /tmp/test.txt; TEST_EXIT=${PIPESTATUS[0]}; echo $TEST_EXIT - # Check if we had fails in the run-tests.sh script - # Exit with the inverted value, because if there are no fails found, it will exit with 1 and for us that\ - # is a good thing so invert it to 0. Travis has some issues with the exclamation mark in front so we have to fiddle a - # bit. - # Also make the grep case insensitive and fail on run-tests.sh regular fails as well on fatal errors. - - TEST_OUTPUT=$(! egrep -i "([0-9]+ fails)|(PHP Fatal error)|([0-9]+ exceptions)" /tmp/test.txt > /dev/null)$? - - echo $TEST_OUTPUT - - cd $TRAVIS_BUILD_DIR/../drupal/core - - ./vendor/bin/phpunit --verbose --debug ../modules/pathauto/; TEST_PHPUNIT=$?; echo $TEST_PHPUNIT - # if the TEST_EXIT status is 0 AND the TEST_OUTPUT status is also 0 it means we succeeded, in all other cases we - # failed. - # Re-enable when trying to get CodeSniffer doesn't return a 403 anymore. - #- /home/travis/.composer/vendor/bin/phpcs --standard=/home/travis/.composer/vendor/drupal/coder/coder_sniffer/Drupal --extensions=php,inc,test,module,install --ignore=css/ $TRAVIS_BUILD_DIR/../drupal/modules/search_api - - php -i | grep 'php.ini' - - sudo cat /var/log/apache2/error.log - # Disabled as it exits with 1 - sudo cat /var/log/syslog | grep 'php' - # Exit the build - - if [ $TEST_EXIT -eq 0 ] && [ $TEST_OUTPUT -eq 0 ] && [ $TEST_PHPUNIT -eq 0 ]; then exit 0; else exit 1; fi + - drupal-ti script + +after_script: + - drupal-ti after_script From f625df75be2848a12a9606aa1de9ffef1cff8c9f Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:30:04 +0200 Subject: [PATCH 041/169] Make error prone --- .travis-before-script.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis-before-script.sh b/.travis-before-script.sh index 8689f31..21dc82f 100644 --- a/.travis-before-script.sh +++ b/.travis-before-script.sh @@ -6,7 +6,12 @@ set -e $DRUPAL_TI_DEBUG # Note: This function is re-entrant. drupal_ti_ensure_drupal -# Download token 8.x-1.x +# Add needed dependencies. cd "$DRUPAL_TI_DRUPAL_DIR" -cd modules + +# These variables come from environments/drupal-*.sh +mkdir -p "$DRUPAL_TI_MODULES_PATH" +cd "$DRUPAL_TI_MODULES_PATH" + +# Download token 8.x-1.x git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git From 46854789271e8fd43969c74881b3c45e788e877e Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:32:43 +0200 Subject: [PATCH 042/169] Use only simpletest now. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa0e823..8f751a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,9 +80,9 @@ env: matrix: # [[[ SELECT ANY OR MORE OPTIONS ]]] #- DRUPAL_TI_RUNNERS="phpunit" - #- DRUPAL_TI_RUNNERS="simpletest" + - DRUPAL_TI_RUNNERS="simpletest" #- DRUPAL_TI_RUNNERS="behat" - - DRUPAL_TI_RUNNERS=" simpletest" + #- DRUPAL_TI_RUNNERS="phpunit simpletest" mysql: database: drupal_travis_db From 6f88b55894ef725ee8a8a2a4f3f8fc9b65af13cb Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:50:04 +0200 Subject: [PATCH 043/169] Add custom runner until upstream supports it. --- .drupal_ti/before/runners/phpunit-core/script.sh | 14 ++++++++++++++ .travis.yml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .drupal_ti/before/runners/phpunit-core/script.sh diff --git a/.drupal_ti/before/runners/phpunit-core/script.sh b/.drupal_ti/before/runners/phpunit-core/script.sh new file mode 100644 index 0000000..5fdacac --- /dev/null +++ b/.drupal_ti/before/runners/phpunit-core/script.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e $DRUPAL_TI_DEBUG + +# Run phpunit with core tests. +# @todo Support directly in drupal-ti via phpunit-core runner for Drupal 8. + +# Find absolute path to module directory. +cd "$DRUPAL_TI_DRUPAL_DIR" +MODULE_DIR=$(cd "$DRUPAL_TI_MODULES_PATH"; pwd) + +# Run core tests +cd core +./vendor/bin/phpunit --verbose --debug "$MODULE_DIR/$DRUPAL_TI_MODULE_NAME" diff --git a/.travis.yml b/.travis.yml index 8f751a4..188947b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ env: # Define runners and environment vars to include before and after the # main runners / environment vars. - #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./drupal_ti/before" + - DRUPAL_TI_SCRIPT_DIR_BEFORE="./.drupal_ti/before" #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" # The environment to use, supported are: drupal-7, drupal-8 From 143ecf734c54dd3b9ae8da663466f8aa700a8a74 Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:51:10 +0200 Subject: [PATCH 044/169] Enable the new runner. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 188947b..cd9579e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,9 +80,9 @@ env: matrix: # [[[ SELECT ANY OR MORE OPTIONS ]]] #- DRUPAL_TI_RUNNERS="phpunit" - - DRUPAL_TI_RUNNERS="simpletest" + #- DRUPAL_TI_RUNNERS="simpletest" #- DRUPAL_TI_RUNNERS="behat" - #- DRUPAL_TI_RUNNERS="phpunit simpletest" + - DRUPAL_TI_RUNNERS="phpunit-core simpletest" mysql: database: drupal_travis_db From d377dfa5387a1303079aa3acc01e00665b78dfd7 Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 00:57:04 +0200 Subject: [PATCH 045/169] Make executable --- .drupal_ti/before/runners/phpunit-core/script.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .drupal_ti/before/runners/phpunit-core/script.sh diff --git a/.drupal_ti/before/runners/phpunit-core/script.sh b/.drupal_ti/before/runners/phpunit-core/script.sh old mode 100644 new mode 100755 From 4aef167498eaf4e42b4500e1138a414ee5c57247 Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 11:15:21 +0200 Subject: [PATCH 046/169] Add comment to trigger re-test --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cd9579e..9c45afa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,6 +98,7 @@ install: - drupal-ti install before_script: + # Install token 8.x-1.x - drupal-ti --include .travis-before-script.sh - drupal-ti before_script From d8e6a9cb16f4049703fd53c3648dfb95d9f557e8 Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Tue, 14 Jul 2015 11:22:39 +0200 Subject: [PATCH 047/169] Use a temporary feature branch for token to make the tests pass --- .travis-before-script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-before-script.sh b/.travis-before-script.sh index 21dc82f..1582474 100644 --- a/.travis-before-script.sh +++ b/.travis-before-script.sh @@ -14,4 +14,4 @@ mkdir -p "$DRUPAL_TI_MODULES_PATH" cd "$DRUPAL_TI_MODULES_PATH" # Download token 8.x-1.x -git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git +git clone --depth 1 --branch 2516454 https://github.com/md-systems/token.git From 12f26187ca6283336d6450f888eee6124090bca6 Mon Sep 17 00:00:00 2001 From: Alex McCabe Date: Tue, 14 Jul 2015 12:03:09 -0400 Subject: [PATCH 048/169] Add leading slashes to paths in routing file. https://github.com/md-systems/pathauto/issues/44 --- pathauto.routing.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pathauto.routing.yml b/pathauto.routing.yml index 92c1efe..ecc5a7b 100644 --- a/pathauto.routing.yml +++ b/pathauto.routing.yml @@ -1,5 +1,5 @@ pathauto.patterns.form: - path: 'admin/config/search/path/patterns' + path: '/admin/config/search/path/patterns' defaults: _form: '\Drupal\pathauto\Form\PathautoPatternsForm' _title: 'Patterns' @@ -7,7 +7,7 @@ pathauto.patterns.form: _permission: 'administer pathauto' pathauto.settings.form: - path: 'admin/config/search/path/settings' + path: '/admin/config/search/path/settings' defaults: _form: '\Drupal\pathauto\Form\PathautoSettingsForm' _title: 'Settings' @@ -15,7 +15,7 @@ pathauto.settings.form: _permission: 'administer pathauto' pathauto.bulk.update.form: - path: 'admin/config/search/path/update_bulk' + path: '/admin/config/search/path/update_bulk' defaults: _form: '\Drupal\pathauto\Form\PathautoBulkUpdateForm' _title: 'Bulk generate' @@ -23,7 +23,7 @@ pathauto.bulk.update.form: _permission: 'administer url aliases' pathauto.admin.delete: - path: 'admin/config/search/path/delete_bulk' + path: '/admin/config/search/path/delete_bulk' defaults: _form: '\Drupal\pathauto\Form\PathautoAdminDelete' _title: 'Delete aliases' From 455fcf0a3f73cc87ec4a2231cf6c1a8e2e5aad59 Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Wed, 15 Jul 2015 01:52:48 -0700 Subject: [PATCH 049/169] Upgrade to 1.3.0, which supports sudo: false, sync dist file. --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c45afa..8770ee5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,7 @@ language: php -# Comment out this line if you only use unit tests. -sudo: required +sudo: false php: - 5.5 @@ -62,6 +61,9 @@ env: - DRUPAL_TI_BEHAT_SCREENSIZE_COLOR="1280x1024x16" # The version of seleniumthat should be used. - DRUPAL_TI_BEHAT_SELENIUM_VERSION="2.44" + # Set DRUPAL_TI_BEHAT_DRIVER to "selenium" to use "firefox" or "chrome" here. + - DRUPAL_TI_BEHAT_DRIVER="phantomjs" + - DRUPAL_TI_BEHAT_BROWSER="firefox" # PHPUnit specific commandline arguments. - DRUPAL_TI_PHPUNIT_ARGS="" From e3c65bd5d2aaaf990b5af120dbf62a1a2e9f016f Mon Sep 17 00:00:00 2001 From: Fabian Franz Date: Sat, 18 Jul 2015 05:58:55 -0700 Subject: [PATCH 050/169] Use upstream phpunit-core support from 1.4.0 or later --- .drupal_ti/before/runners/phpunit-core/script.sh | 14 -------------- .travis.yml | 8 ++++++-- 2 files changed, 6 insertions(+), 16 deletions(-) delete mode 100755 .drupal_ti/before/runners/phpunit-core/script.sh diff --git a/.drupal_ti/before/runners/phpunit-core/script.sh b/.drupal_ti/before/runners/phpunit-core/script.sh deleted file mode 100755 index 5fdacac..0000000 --- a/.drupal_ti/before/runners/phpunit-core/script.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -e $DRUPAL_TI_DEBUG - -# Run phpunit with core tests. -# @todo Support directly in drupal-ti via phpunit-core runner for Drupal 8. - -# Find absolute path to module directory. -cd "$DRUPAL_TI_DRUPAL_DIR" -MODULE_DIR=$(cd "$DRUPAL_TI_MODULES_PATH"; pwd) - -# Run core tests -cd core -./vendor/bin/phpunit --verbose --debug "$MODULE_DIR/$DRUPAL_TI_MODULE_NAME" diff --git a/.travis.yml b/.travis.yml index 8770ee5..112aea1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ env: # Define runners and environment vars to include before and after the # main runners / environment vars. - - DRUPAL_TI_SCRIPT_DIR_BEFORE="./.drupal_ti/before" + #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./.drupal_ti/before" #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" # The environment to use, supported are: drupal-7, drupal-8 @@ -66,7 +66,11 @@ env: - DRUPAL_TI_BEHAT_BROWSER="firefox" # PHPUnit specific commandline arguments. - - DRUPAL_TI_PHPUNIT_ARGS="" + - DRUPAL_TI_PHPUNIT_ARGS="--verbose --debug" + # Specifying the phpunit-core src/ directory is useful when e.g. a vendor/ + # directory is present in the module directory, which phpunit would then + # try to find tests in. This option is relative to $TRAVIS_BUILD_DIR. + #- DRUPAL_TI_PHPUNIT_CORE_SRC_DIRECTORY="./tests/src" # Code coverage via coveralls.io - DRUPAL_TI_COVERAGE="satooshi/php-coveralls:0.6.*" From ea9d691fcae311d1b295198ab7b31c124de994a1 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 28 Jul 2015 18:45:57 +0200 Subject: [PATCH 051/169] Avoid errors if an entity has no canonical link template --- pathauto.module | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pathauto.module b/pathauto.module index 18a8034..d91c458 100644 --- a/pathauto.module +++ b/pathauto.module @@ -136,7 +136,9 @@ function pathauto_entity_update(EntityInterface $entity) { * Implements hook_entity_update(). */ function pathauto_entity_delete(EntityInterface $entity) { - \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity); + if ($entity->hasLinkTemplate('canonical')) { + \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity); + } } /** From 85cbf663d747512d9abfe918b51a39e74e696981 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 1 Aug 2015 21:17:09 +0200 Subject: [PATCH 052/169] deleteEntityPathAll() needs to use getInternalPath() --- src/AliasStorageHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AliasStorageHelper.php b/src/AliasStorageHelper.php index 2d848bd..7114400 100644 --- a/src/AliasStorageHelper.php +++ b/src/AliasStorageHelper.php @@ -190,7 +190,7 @@ public function deleteAll($source) { * {@inheritdoc} */ public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL) { - $this->deleteAll($entity->urlInfo()->toString()); + $this->deleteAll('/'. $entity->urlInfo()->getInternalPath()); if (isset($default_uri) && $entity->urlInfo()->toString() != $default_uri) { $this->deleteAll($default_uri); } From 63927e340b65f2d0e1a8b722db0fed4e5c6b451f Mon Sep 17 00:00:00 2001 From: mbovan Date: Fri, 24 Jul 2015 17:45:56 +0200 Subject: [PATCH 053/169] Added bubbleable metadata parameter into pathauto_tokens(). --- pathauto.tokens.inc | 3 ++- src/Tests/PathautoTestHelperTrait.php | 4 +++- src/Tests/PathautoTokenTest.php | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pathauto.tokens.inc b/pathauto.tokens.inc index 5660c67..06c8c93 100644 --- a/pathauto.tokens.inc +++ b/pathauto.tokens.inc @@ -6,6 +6,7 @@ */ use Drupal\Core\Render\Element; +use Drupal\Core\Render\BubbleableMetadata; /** * Implements hook_token_info(). @@ -24,7 +25,7 @@ function pathauto_token_info() { /** * Implements hook_tokens(). */ -function pathauto_tokens($type, $tokens, array $data = array(), array $options = array()) { +function pathauto_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { $replacements = array(); if ($type == 'array' && !empty($data['array'])) { diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 7aee60f..4dec728 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Render\BubbleableMetadata; use Drupal\taxonomy\VocabularyInterface; /** @@ -18,7 +19,8 @@ trait PathautoTestHelperTrait { public function assertToken($type, $object, $token, $expected) { - $tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object)); + $bubbleable_metadata = new BubbleableMetadata(); + $tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object), [], $bubbleable_metadata); $tokens += array($token => ''); $this->assertIdentical($tokens[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $tokens[$token], '@expected' => $expected))); } diff --git a/src/Tests/PathautoTokenTest.php b/src/Tests/PathautoTokenTest.php index fbca356..8c298e5 100644 --- a/src/Tests/PathautoTokenTest.php +++ b/src/Tests/PathautoTokenTest.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto\Tests; +use Drupal\Core\Render\BubbleableMetadata; use Drupal\simpletest\KernelTestBase; /** @@ -50,7 +51,8 @@ public function testPathautoTokens() { */ public function assertTokens($type, array $data, array $tokens, array $options = array()) { $input = $this->mapTokenNames($type, array_keys($tokens)); - $replacements = \Drupal::token()->generate($type, $input, $data, $options); + $bubbleable_metadata = new BubbleableMetadata(); + $replacements = \Drupal::token()->generate($type, $input, $data, $options, $bubbleable_metadata); foreach ($tokens as $name => $expected) { $token = $input[$name]; if (!isset($expected)) { From 6d8366b5c60534943f5677c5d77939cdb34a7a7b Mon Sep 17 00:00:00 2001 From: mbovan Date: Fri, 24 Jul 2015 17:46:43 +0200 Subject: [PATCH 054/169] Token branch changed to feature (token-metadata) branch for testing reasons. --- .travis-before-script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-before-script.sh b/.travis-before-script.sh index 1582474..21dc82f 100644 --- a/.travis-before-script.sh +++ b/.travis-before-script.sh @@ -14,4 +14,4 @@ mkdir -p "$DRUPAL_TI_MODULES_PATH" cd "$DRUPAL_TI_MODULES_PATH" # Download token 8.x-1.x -git clone --depth 1 --branch 2516454 https://github.com/md-systems/token.git +git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git From 5bca6224f17ba1fc2b97bcc8a9f0bb2ed0182be1 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 5 Aug 2015 18:55:40 +0200 Subject: [PATCH 055/169] Workaround to prevent endless loop for repeatedly matching route. --- src/AliasUniquifier.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 16285c9..82c18fb 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -56,6 +56,15 @@ class AliasUniquifier implements AliasUniquifierInterface { */ protected $aliasManager; + /** + * Stores the last matching route name. + * + * Used to prevent a loop if the same route matches a given pattern. + * + * @var + */ + protected $lastRouteName; + /** * Creates a new AliasUniquifier. * @@ -147,6 +156,8 @@ public function isReserved($alias, $source, $langcode = LanguageInterface::LANGC * * @return bool * TRUE if the path already exists. + * + * @throws \InvalidArgumentException */ public function isRoute($path) { if (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) { @@ -157,10 +168,17 @@ public function isRoute($path) { } try { - $this->urlMatcher->match($path); + $route = $this->urlMatcher->match($path); + + if ($route['_route'] == $this->lastRouteName) { + throw new \InvalidArgumentException('The given alias pattern (' . $path . ') always matches the route ' . $this->lastRouteName); + } + + $this->lastRouteName = $route['_route']; return TRUE; } catch (ResourceNotFoundException $e) { + $this->lastRouteName = NULL; return FALSE; } } From 56ca6f5cc831e361666f8776bd888867a8952b8b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 6 Aug 2015 22:31:40 +0200 Subject: [PATCH 056/169] Update config schema --- config/schema/pathauto.schema.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/schema/pathauto.schema.yml b/config/schema/pathauto.schema.yml index f3f2bc7..917e674 100644 --- a/config/schema/pathauto.schema.yml +++ b/config/schema/pathauto.schema.yml @@ -1,5 +1,5 @@ pathauto.pattern: - type: mapping + type: config_object mapping: patterns: type: sequence @@ -22,7 +22,7 @@ pathauto.pattern: pathauto.settings: - type: mapping + type: config_object mapping: punctuation: type: sequence From 02e5b3cad0180f6ba35352113d07db876c8dd6f1 Mon Sep 17 00:00:00 2001 From: Dave Reid Date: Sun, 9 Aug 2015 13:22:10 -0500 Subject: [PATCH 057/169] Fixed _pathauto_alias_exists(). --- pathauto.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pathauto.inc b/pathauto.inc index 39e50b6..db8d066 100644 --- a/pathauto.inc +++ b/pathauto.inc @@ -61,13 +61,14 @@ define('PATHAUTO_PUNCTUATION_DO_NOTHING', 2); * A string that is the internal path. * @param $langcode * A string indicating the path's language. - * @return + * + * @return bool * TRUE if an alias exists, FALSE if not. * * @deprecated Use path_pathauto_is_alias_reserved() instead. */ function _pathauto_alias_exists($alias, $source, $langcode = LANGUAGE_NONE) { - return path_pathauto_is_alias_reserved(); + return path_pathauto_is_alias_reserved($alias, $source, $langcode); } /** From 110be4c3d68c8f541cdf29f5bbbbcfa28bb647ee Mon Sep 17 00:00:00 2001 From: Dave Reid Date: Sun, 9 Aug 2015 14:01:43 -0500 Subject: [PATCH 058/169] Fixed docs in pathauto.inc. --- pathauto.inc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pathauto.inc b/pathauto.inc index db8d066..1081476 100644 --- a/pathauto.inc +++ b/pathauto.inc @@ -74,11 +74,12 @@ function _pathauto_alias_exists($alias, $source, $langcode = LANGUAGE_NONE) { /** * Fetches an existing URL alias given a path and optional language. * - * @param $source + * @param string $source * An internal Drupal path. - * @param $language + * @param string $language * An optional language code to look up the path in. - * @return + * + * @return bool|array * FALSE if no alias was found or an associative array containing the * following keys: * - pid: Unique path alias identifier. @@ -107,14 +108,14 @@ function _pathauto_existing_alias_data($source, $language = LANGUAGE_NONE) { * This function should *not* be called on URL alias or path strings because it * is assumed that they are already clean. * - * @param $string + * @param string $string * A string to clean. * @param array $options * (optional) A keyed array of settings and flags to control the Pathauto * clean string replacement process. Supported options are: * - langcode: A language code to be used when translating strings. * - * @return + * @return string * The cleaned string. */ function pathauto_cleanstring($string, array $options = array()) { @@ -240,11 +241,12 @@ function pathauto_cleanstring($string, array $options = array()) { /** * Trims duplicate, leading, and trailing separators from a string. * - * @param $string + * @param string $string * The string to clean path separators from. - * @param $separator + * @param string $separator * The path separator to use when cleaning. - * @return + * + * @return string * The cleaned version of the string. * * @see pathauto_cleanstring() @@ -289,9 +291,10 @@ function _pathauto_clean_separators($string, $separator = NULL) { * - Trim duplicate, leading, and trailing separators. * - Shorten to a desired length and logical position based on word boundaries. * - * @param $alias + * @param string $alias * A string with the URL alias to clean up. - * @return + * + * @return string * The cleaned URL alias. */ function pathauto_clean_alias($alias) { From 5ff61d8c92fc2e008caae3462daedbf3bd6a410f Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 22 Aug 2015 15:28:54 +0200 Subject: [PATCH 059/169] Fix the unit tests --- src/Tests/PathautoUnitTest.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index dd996aa..23d5add 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Language\Language; +use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; use Drupal\simpletest\KernelTestBase; @@ -28,7 +29,7 @@ class PathautoUnitTest extends KernelTestBase { public function setUp() { parent::setup(); - $this->installConfig(array('pathauto', 'taxonomy', 'system')); + $this->installConfig(array('pathauto', 'taxonomy', 'system', 'node')); $this->installEntitySchema('user'); $this->installEntitySchema('node'); @@ -37,6 +38,10 @@ public function setUp() { $this->installSchema('node', array('node_access')); $this->installSchema('system', array('url_alias', 'sequences', 'router')); + $type = NodeType::create(['type' => 'page']); + $type->save(); + node_add_body_field($type); + \Drupal::service('router.builder')->rebuild(); $this->currentUser = entity_create('user', array('name' => $this->randomMachineName())); @@ -250,10 +255,14 @@ public function testUpdateActions() { * that does not get any tokens replaced. */ public function testNoTokensNoAlias() { - $node = $this->drupalCreateNode(array('title' => '')); + $config = $this->config('pathauto.pattern'); + $config->set('patterns.node.default', '/content/[node:body]'); + $config->save(); + + $node = $this->drupalCreateNode(); $this->assertNoEntityAliasExists($node); - $node->setTitle('hello'); + $node->body->value = 'hello'; $node->save(); $this->assertEntityAlias($node, '/content/hello'); } From d50cb54c1b09bc13d39d27561be494059355b2fc Mon Sep 17 00:00:00 2001 From: Samuel Mortenson Date: Tue, 25 Aug 2015 08:20:05 -0700 Subject: [PATCH 060/169] Return empty metadata when calling Token::replace(). --- src/PathautoManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 4754fb2..211b29a 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -16,6 +16,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\Utility\Token; @@ -372,7 +373,7 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod 'callback' => array($this, 'cleanTokenValues'), 'langcode' => $langcode, 'pathauto' => TRUE, - )); + ), new BubbleableMetadata()); // Check if the token replacement has not actually replaced any values. If // that is the case, then stop because we should not generate an alias. From f6a2d0a3974cef5b460fe34f75eb4bf906ac1dab Mon Sep 17 00:00:00 2001 From: Samuel Mortenson Date: Tue, 25 Aug 2015 09:04:12 -0700 Subject: [PATCH 061/169] Added comment to explain our use of empty BubbleableMetadata. --- src/PathautoManager.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 211b29a..31bfd31 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -367,6 +367,8 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod // Replace any tokens in the pattern. // Uses callback option to clean replacements. No sanitization. + // Pass empty BubbleableMetadata object to explicitly ignore cacheablity, + // as the result is never rendered. $alias = $this->token->replace($pattern, $data, array( 'sanitize' => FALSE, 'clear' => TRUE, From a46a4c60a5617ac9e26e2bbeba64dd01bd22a6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20St=C3=B6ckler?= Date: Tue, 1 Sep 2015 15:14:03 +0200 Subject: [PATCH 062/169] Make PathautoPatternsForm::buildForm() account for missing config The default pattern config shipped with the module cannot account for alias types provided by other modules (e.g. File Entity). We cannot use the ternary in createInstance() directly and instead need to call setConfiguration() because the defaultConfiguration() would be ignored otherwise. --- src/Form/PathautoPatternsForm.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Form/PathautoPatternsForm.php b/src/Form/PathautoPatternsForm.php index c327df3..a711c4c 100644 --- a/src/Form/PathautoPatternsForm.php +++ b/src/Form/PathautoPatternsForm.php @@ -71,7 +71,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { foreach ($definitions as $id => $definition) { /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ - $alias_type = $this->aliasTypeManager->createInstance($id, $config->get('patterns.' . $id)); + $alias_type = $this->aliasTypeManager->createInstance($id); + $alias_type->setConfiguration($config->get('patterns.' . $id) ?: []); $form[$id] = $alias_type->buildConfigurationForm([], $form_state); } From d4422eb2a792e1e15d80df440de9e70586ffd6b5 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 24 Sep 2015 00:14:02 +0200 Subject: [PATCH 063/169] Remove useless INSTALL.txt and TODO.txt --- INSTALL.txt | 48 ------------------------------------------------ TODO.txt | 9 --------- 2 files changed, 57 deletions(-) delete mode 100644 INSTALL.txt delete mode 100644 TODO.txt diff --git a/INSTALL.txt b/INSTALL.txt deleted file mode 100644 index 586fdfe..0000000 --- a/INSTALL.txt +++ /dev/null @@ -1,48 +0,0 @@ -**Installation: - -Pathauto is an extension to the path module, which must be enabled. - -Pathauto also relies on the Token module, which must be downloaded and -enabled separately. - -1. Unpack the Pathauto folder and contents in the appropriate modules -directory of your Drupal installation. This is probably - sites/all/modules/ -2. Enable the Pathauto module in the administration tools. -3. If you're not using Drupal's default administrative account, make -sure "administer pathauto" is enabled through access control administration. -4. Visit the Pathauto settings page and make appropriate configurations - For 5.x: Administer > Site configuration > Pathauto - For 6.x: Administer > Site building > URL alias > Automated alias settings - -**Transliteration support: -If you desire transliteration support in the creation of URLs (e.g. the -replacement of Á with A) then you will need to install the Transliteration -module, which can be found at http://drupal.org/project/transliteration - -Once you've installed and enabled the module, simply go to -admin/config/search/path/settings and check the "Transliterate prior to -creating alias" box and path aliases should now be transliterated automagically. - -**Upgrading from previous versions: -If you are upgrading from Pathauto 5.x-1.x to 5.x-2.x (or 6.x-2.x) then you -will probably need to change your patterns. - -For content patterns: - [user] is now [author-name] - [cat] is now [term] - -There may be other changes as well. Please review the pattern examples on - Administration > Site Configuration > Pathauto - -If you upgraded from Pathauto 5.x-1.x directly without enabling Token -first then you will need to - 1) download/install the Token module - 2) disable the Pathauto module - 3) re-enable the Pathauto module - -Upgrade to 6.x: -Note that the settings page has moved so that it is more logically grouped with -other URL alias related items under - Administer > Site building > URL alias > Automated alias settings - diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 7c5ca19..0000000 --- a/TODO.txt +++ /dev/null @@ -1,9 +0,0 @@ -- Creating a new node with a manually set alias and not unchecking - the checkbox results in two aliases, one saved by pathauto and one by - PathItem. A possible clean way to do this might be to replace the PathItem - class with a ustom one and completely replace the logic there. Or fix core to - make sure that it sets ->pid after saving a new alias. - -- pathauto_path_alias_types() => Plugins - -- INSTALL.txt: Update From 519bc0df343d0dc3eccf311e42366f70470828d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20St=C3=B6ckler?= Date: Thu, 24 Sep 2015 18:12:44 +0200 Subject: [PATCH 064/169] Make EntityAliasTypeBase::__construct() call $this->setConfiguration() This is inline with BlockBase in core. Without this defaultConfiguration() is not (always) merged with the passed-in configuration. This allows to avoid the workaround added in the previous commit. --- src/Form/PathautoPatternsForm.php | 3 +-- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Form/PathautoPatternsForm.php b/src/Form/PathautoPatternsForm.php index a711c4c..a2b871b 100644 --- a/src/Form/PathautoPatternsForm.php +++ b/src/Form/PathautoPatternsForm.php @@ -71,8 +71,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { foreach ($definitions as $id => $definition) { /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ - $alias_type = $this->aliasTypeManager->createInstance($id); - $alias_type->setConfiguration($config->get('patterns.' . $id) ?: []); + $alias_type = $this->aliasTypeManager->createInstance($id, $config->get('patterns.' . $id) ?: []); $form[$id] = $alias_type->buildConfigurationForm([], $form_state); } diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 7277a91..8ca4ae0 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -63,6 +63,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->moduleHandler = $module_handler; $this->languageManager = $language_manager; $this->entityManager = $entity_manager; + $this->setConfiguration($configuration); } /** From 7f2c30b1a952dd15f1a98f898f2df2f8cd670a26 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 27 Sep 2015 17:04:34 +0200 Subject: [PATCH 065/169] Update deprecated t() placeholders --- src/Form/PathautoSettingsForm.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 78647fe..5b4ec53 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -102,10 +102,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { $description = t('What should Pathauto do when updating an existing content item which already has an alias?'); if (\Drupal::moduleHandler()->moduleExists('redirect')) { - $description .= ' ' . t('The Redirect module settings affect whether a redirect is created when an alias is deleted.', array('!url' => \Drupal::url('redirect.settings'))); + $description .= ' ' . t('The Redirect module settings affect whether a redirect is created when an alias is deleted.', array(':url' => \Drupal::url('redirect.settings'))); } else { - $description .= ' ' . t('Considering installing the Redirect module to get redirects when your aliases change.', array('!url' => 'http://drupal.org/project/redirect')); + $description .= ' ' . t('Considering installing the Redirect module to get redirects when your aliases change.', array('!url' => 'http://drupal.org/project/redirect')); } $form['update_action'] = array( From 3350940ebd502826aff000b1c493673059e9aae6 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 27 Sep 2015 19:29:28 +0200 Subject: [PATCH 066/169] Update deprecated t() placeholders --- src/Form/PathautoSettingsForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 5b4ec53..ac30257 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -105,7 +105,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $description .= ' ' . t('The Redirect module settings affect whether a redirect is created when an alias is deleted.', array(':url' => \Drupal::url('redirect.settings'))); } else { - $description .= ' ' . t('Considering installing the Redirect module to get redirects when your aliases change.', array('!url' => 'http://drupal.org/project/redirect')); + $description .= ' ' . t('Considering installing the Redirect module to get redirects when your aliases change.', array(':url' => 'http://drupal.org/project/redirect')); } $form['update_action'] = array( From 5bae990c85a1fbe4a5b2b6f1c7ae8489147acad5 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 3 Oct 2015 15:12:47 +0200 Subject: [PATCH 067/169] Cast strings where necessary, remove bundle rename test --- pathauto.module | 15 --------------- src/PathautoManager.php | 6 +++--- src/Tests/PathautoUnitTest.php | 20 +++++++------------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/pathauto.module b/pathauto.module index d91c458..ce2d79d 100644 --- a/pathauto.module +++ b/pathauto.module @@ -64,21 +64,6 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { } } -/** - * Implements hook_entity_bundle_rename(). - */ -function pathauto_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) { - $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); - $bundle_settings = $config->get('patterns.' . $entity_type_id . '.bundles'); - - if (isset($bundle_settings[$bundle_old])) { - $bundle_settings[$bundle_new] = $bundle_settings[$bundle_old]; - unset($bundle_settings[$bundle_old]); - $config->set('patterns.' . $entity_type_id . '.bundles', $bundle_settings); - $config->save(); - } -} - /** * Implements hook__entity_bundle_delete(). */ diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 0742572..60dbe14 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -217,8 +217,8 @@ public function cleanString($string, array $options = array()) { // Check if the string has already been processed, and if so return the // cached result. - if (isset($this->cleanStringCache['strings'][$langcode][$string])) { - return $this->cleanStringCache['strings'][$langcode][$string]; + if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) { + return $this->cleanStringCache['strings'][$langcode][(string) $string]; } // Remove all HTML tags from the string. @@ -263,7 +263,7 @@ public function cleanString($string, array $options = array()) { $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE); // Cache this result in the static array. - $this->cleanStringCache['strings'][$langcode][$string] = $output; + $this->cleanStringCache['strings'][$langcode][(string) $string] = $output; return $output; } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 23d5add..18e7efb 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -139,7 +139,7 @@ public function testCleanString() { // Test that HTML tags are removed. $tests['This text has
HTML tags.'] = 'text-has-html-tags'; - $tests[SafeMarkup::checkPlain('This text has
HTML tags.')] = 'text-has-html-tags'; + $tests[(string) SafeMarkup::checkPlain('This text has
HTML tags.')] = 'text-has-html-tags'; // Transliteration. $tests['ľščťžýáíéňô'] = 'lsctzyaieno'; @@ -255,6 +255,7 @@ public function testUpdateActions() { * that does not get any tokens replaced. */ public function testNoTokensNoAlias() { + $this->installConfig(['filter']); $config = $this->config('pathauto.pattern'); $config->set('patterns.node.default', '/content/[node:body]'); $config->save(); @@ -288,28 +289,21 @@ public function testPathTokens() { $this->assertEntityAlias($term2, '/My Crazy/Alias/child-term'); } - public function testEntityBundleRenamingDeleting() { + public function testEntityBundleDeleting() { $config = $this->config('pathauto.pattern'); // Create a vocabulary and test that it's pattern variable works. - $vocab = $this->addVocabulary(array('vid' => 'old_name')); + $vocab = $this->addVocabulary(array('vid' => 'name')); $config->set('patterns.taxonomy_term.default', 'base'); - $config->set('patterns.taxonomy_term.bundles.old_name.default', 'bundle'); + $config->set('patterns.taxonomy_term.bundles.name.default', 'bundle'); $config->save(); - $this->assertEntityPattern('taxonomy_term', 'old_name', Language::LANGCODE_NOT_SPECIFIED, 'bundle'); - - // Rename the vocabulary's machine name, which should cause its pattern - // variable to also be renamed. - $vocab->set('vid', 'new_name'); - $vocab->save(); - $this->assertEntityPattern('taxonomy_term', 'new_name', Language::LANGCODE_NOT_SPECIFIED, 'bundle'); - $this->assertEntityPattern('taxonomy_term', 'old_name', Language::LANGCODE_NOT_SPECIFIED, 'base'); + $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'bundle'); // Delete the vocabulary, which should cause its pattern variable to also // be deleted. $vocab->delete(); - $this->assertEntityPattern('taxonomy_term', 'new_name', Language::LANGCODE_NOT_SPECIFIED, 'base'); + $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'base'); } function testNoExistingPathAliases() { From fcf8c2fcd4812898525cd9d02db748effd765b4e Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 3 Oct 2015 15:23:40 +0200 Subject: [PATCH 068/169] Remove sanitize option, use PlainTextOutput --- src/PathautoManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 60dbe14..9a3b70b 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto; +use Drupal\Component\Render\PlainTextOutput; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\CacheBackendInterface; @@ -222,7 +223,7 @@ public function cleanString($string, array $options = array()) { } // Remove all HTML tags from the string. - $output = strip_tags(Html::decodeEntities($string)); + $output = PlainTextOutput::renderFromHtml($string); // Optionally transliterate. if ($this->cleanStringCache['transliterate']) { @@ -370,7 +371,6 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod // Pass empty BubbleableMetadata object to explicitly ignore cacheablity, // as the result is never rendered. $alias = $this->token->replace($pattern, $data, array( - 'sanitize' => FALSE, 'clear' => TRUE, 'callback' => array($this, 'cleanTokenValues'), 'langcode' => $langcode, From e850af1d33adecd68c9e36274a7b6391a89125eb Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 8 Oct 2015 01:50:45 +0200 Subject: [PATCH 069/169] Remove usage of generateFromPath() --- src/Form/PathautoSettingsForm.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index ac30257..b8d8bcd 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\Url; use Drupal\pathauto\PathautoManagerInterface; use Drupal\Core\Form\FormStateInterface; @@ -78,6 +79,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { $max_length = \Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength(); + $help_link = ''; + if (\Drupal::moduleHandler()->moduleExists('help')) { + $help_link = ' ' . t('See Pathauto help for details.', [':pathauto-help' => Url::fromRoute('help.page', ['name' => 'pathauto'])->toString()]); + } + $form['max_length'] = array( '#type' => 'number', '#title' => t('Maximum alias length'), @@ -86,7 +92,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('max_length'), '#min' => 1, '#max' => $max_length, - '#description' => t('Maximum length of aliases to generate. 100 is the recommended length. @max is the maximum possible length. See Pathauto help for details.', array('@pathauto-help' => $this->getUrlGenerator()->generateFromPath('admin/help/pathauto'), '@max' => $max_length)), + '#description' => t('Maximum length of aliases to generate. 100 is the recommended length. @max is the maximum possible length.', array('@max' => $max_length)) . $help_link, ); $form['max_component_length'] = array( @@ -97,7 +103,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('max_component_length'), '#min' => 1, '#max' => $max_length, - '#description' => t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length. See Pathauto help for details.', array('@pathauto-help' => $this->getUrlGenerator()->generateFromPath('admin/help/pathauto'), '@max' => $max_length)), + '#description' => t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length.', ['@max' => $max_length]) . $help_link, ); $description = t('What should Pathauto do when updating an existing content item which already has an alias?'); From e5a2a2aa3d69e0f09d2e5e1b964752142ab66e12 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 19 Oct 2015 21:20:01 +0200 Subject: [PATCH 070/169] Issue #2067191 by bneil, Martijn Houtman, stefan.r, rv0, DamienMcKenna, Dave Reid: Fixed path_pathauto_is_alias_reserved() does not handle language-neutral aliases correctly, possibly resulting in duplicate aliases (tests only) --- src/Tests/PathautoLocaleTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index bfb0258..1a1d4f9 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -8,6 +8,7 @@ namespace Drupal\pathauto\Tests; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageInterface; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\simpletest\WebTestBase; @@ -71,6 +72,14 @@ function testLanguageAliases() { $this->assertEntityAlias($node, '/content/english-node-0', 'en'); $this->assertEntityAlias($node, '/french-node', 'fr'); $this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => '/content/english-node-0')); + + // Create a new node with the same title as before but without + // specifying a language. + $node = $this->drupalCreateNode(array('title' => 'English node', 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED)); + + // Check that the new node had a unique alias generated with the '-1' + // suffix. + $this->assertEntityAlias($node, '/content/english-node-1', LanguageInterface::LANGCODE_NOT_SPECIFIED); } } From 7b622269056a7a3485cfefe1838b94d9da6023e6 Mon Sep 17 00:00:00 2001 From: mfb Date: Fri, 2 Oct 2015 00:59:32 -0500 Subject: [PATCH 071/169] Issue #2372101 by mfb: Fixed mb_eregi_replace() called without first calling mb_regex_encoding() and setting encoding to UTF-8. --- src/PathautoManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 9a3b70b..eed8f2d 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -194,6 +194,7 @@ public function cleanString($string, array $options = array()) { if ($ignore_words_regex) { $this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b'; if (function_exists('mb_eregi_replace')) { + mb_regex_encoding('UTF-8'); $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace'; } else { From 2bb457c724d35acdada7e02e3a391548160ff471 Mon Sep 17 00:00:00 2001 From: mtift Date: Fri, 2 Oct 2015 01:17:16 -0500 Subject: [PATCH 072/169] Issue #2434675 by mtift: Clarify when hook_pathauto_pattern_alter() is called. --- pathauto.api.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pathauto.api.php b/pathauto.api.php index a714ffd..4af9023 100644 --- a/pathauto.api.php +++ b/pathauto.api.php @@ -89,6 +89,9 @@ function hook_pathauto_is_alias_reserved($alias, $source, $langcode) { /** * Alter the pattern to be used before an alias is generated by Pathauto. * + * This hook will only be called if a default pattern is configured (on + * admin/config/search/path/patterns). + * * @param string $pattern * The alias pattern for Pathauto to pass to token_replace() to generate the * URL alias. From 7bdd775722830b9943f2d89e43f10e3007dfcc62 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 19 Oct 2015 21:45:44 +0200 Subject: [PATCH 073/169] Issue #2423077 by Dave Reid, TuWebO: Fixed wrong parameters passed to truncate_utf8() from pathauto_alias_uniquify(). --- src/AliasCleaner.php | 15 +++------------ src/AliasUniquifier.php | 2 +- src/Tests/PathautoUnitTest.php | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index 565f31d..3a185dd 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -29,13 +29,6 @@ class AliasCleaner implements AliasCleanerInterface { */ protected $aliasStorageHelper; - /** - * Alias max length. - * - * @var int - */ - protected $aliasMaxLength; - /** * Creates a new AliasCleaner. * @@ -53,10 +46,8 @@ public function __construct(ConfigFactoryInterface $config_factory, AliasStorage * {@inheritdoc} */ public function cleanAlias($alias) { - if (!isset($this->aliasMaxLength)) { - $config = $this->configFactory->get('pathauto.settings'); - $this->aliasMaxLength = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()); - } + $config = $this->configFactory->get('pathauto.settings'); + $alias_max_length = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()); $output = $alias; @@ -70,7 +61,7 @@ public function cleanAlias($alias) { $output = $this->getCleanSeparators($output, '/'); // Shorten to a logical place based on word boundaries. - $output = Unicode::truncate($output, $this->aliasMaxLength, TRUE); + $output = Unicode::truncate($output, $alias_max_length, TRUE); return $output; } diff --git a/src/AliasUniquifier.php b/src/AliasUniquifier.php index 82c18fb..c430f0a 100644 --- a/src/AliasUniquifier.php +++ b/src/AliasUniquifier.php @@ -104,7 +104,7 @@ public function uniquify(&$alias, $source, $langcode) { do { // Append an incrementing numeric suffix until we find a unique alias. $unique_suffix = $separator . $i; - $alias = Unicode::truncate($original_alias, $maxlength - Unicode::strlen($unique_suffix, TRUE)) . $unique_suffix; + $alias = Unicode::truncate($original_alias, $maxlength - Unicode::strlen($unique_suffix), TRUE) . $unique_suffix; $i++; } while ($this->isReserved($alias, $source, $langcode)); } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 18e7efb..7e9259e 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -358,6 +358,24 @@ function testProgrammaticEntityCreation() { $this->assertEntityAlias($account, '/users/test-user'); } + /** + * Tests word safe alias truncating truncating. + */ + function testPathAliasUniquifyWordsafe() { + $this->config('pathauto.settings') + ->set('max_length', 26) + ->save(); + + $node_1 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page')); + $node_2 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page')); + + // Check that alias uniquifying is truncating with $wordsafe param set to + // TRUE. + // If it doesn't path alias result would be content/thequick-brownf-0 + $this->assertEntityAlias($node_1, '/content/thequick-brownfox'); + $this->assertEntityAlias($node_2, '/content/thequick-0'); + } + protected function drupalCreateNode(array $settings = array()) { // Populate defaults array. $settings += array( From 7657d5d321bec34452480738d255246f8a3fa7bc Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 19 Oct 2015 18:09:04 -0500 Subject: [PATCH 074/169] typo fix --- pathauto.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.module b/pathauto.module index ce2d79d..4a4f136 100644 --- a/pathauto.module +++ b/pathauto.module @@ -65,7 +65,7 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { } /** - * Implements hook__entity_bundle_delete(). + * Implements hook_entity_bundle_delete(). */ function pathauto_entity_bundle_delete($entity_type, $bundle) { $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); From abb7881da8fbb44623cfa17f5ba48f63fb4224c3 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 19 Oct 2015 18:17:13 -0500 Subject: [PATCH 075/169] initial commit of config entity to hold pattern settings --- config/schema/pathauto_pattern.schema.yml | 30 ++++ src/Entity/PathautoPattern.php | 208 ++++++++++++++++++++++ src/PathautoPatternInterface.php | 101 +++++++++++ 3 files changed, 339 insertions(+) create mode 100644 config/schema/pathauto_pattern.schema.yml create mode 100644 src/Entity/PathautoPattern.php create mode 100644 src/PathautoPatternInterface.php diff --git a/config/schema/pathauto_pattern.schema.yml b/config/schema/pathauto_pattern.schema.yml new file mode 100644 index 0000000..c42065f --- /dev/null +++ b/config/schema/pathauto_pattern.schema.yml @@ -0,0 +1,30 @@ +pathauto.pathauto_pattern.*: + type: config_entity + label: 'Pathauto pattern config' + mapping: + id: + type: string + label: 'ID' + label: + type: label + label: 'Label' + uuid: + type: string + type: + type: string + label: 'Pattern type' + pattern: + type: string + label: 'Pattern' + selection_criteria: + type: sequence + label: 'Selection criteria' + sequence: + - type: condition.plugin.[id] + label: 'Selection condition' + selection_logic: + type: string + label: 'Selection logic' + weight: + type: integer + label: 'Weight' diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php new file mode 100644 index 0000000..c7c564e --- /dev/null +++ b/src/Entity/PathautoPattern.php @@ -0,0 +1,208 @@ +getSelectionConditions() as $id => $condition) { + $criteria[$id] = $condition->getConfiguration(); + } + $this->selection_criteria = $criteria; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + + foreach ($this->getSelectionConditions() as $instance) { + $this->calculatePluginDependencies($instance); + } + + return $this->getDependencies(); + } + + /** + * {@inheritdoc} + */ + public function getPattern() { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function setPattern($pattern) { + $this->pattern = $pattern; + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->type; + } + + /** + * {@inheritdoc} + */ + public function getWeight() { + return $this->weight; + } + + /** + * {@inheritdoc} + */ + public function setWeight($weight) { + $this->weight = $weight; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSelectionConditions() { + if (!$this->selectionConditionCollection) { + $this->selectionConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this->get('selection_criteria')); + } + return $this->selectionConditionCollection; + } + + /** + * {@inheritdoc} + */ + public function addSelectionCondition(array $configuration) { + $configuration['uuid'] = $this->uuidGenerator()->generate(); + $this->getSelectionConditions()->addInstanceId($configuration['uuid'], $configuration); + return $configuration['uuid']; + } + + /** + * {@inheritdoc} + */ + public function getSelectionCondition($condition_id) { + return $this->getSelectionConditions()->get($condition_id); + } + + /** + * {@inheritdoc} + */ + public function removeSelectionCondition($condition_id) { + $this->getSelectionConditions()->removeInstanceId($condition_id); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSelectionLogic() { + return $this->selection_logic; + } + +} diff --git a/src/PathautoPatternInterface.php b/src/PathautoPatternInterface.php new file mode 100644 index 0000000..bd5bcb0 --- /dev/null +++ b/src/PathautoPatternInterface.php @@ -0,0 +1,101 @@ + Date: Tue, 20 Oct 2015 08:23:21 +0200 Subject: [PATCH 076/169] Alter the default path widget class instead of defining our own --- pathauto.module | 16 +++++----------- .../Field/FieldWidget => }/PathautoWidget.php | 15 +++------------ 2 files changed, 8 insertions(+), 23 deletions(-) rename src/{Plugin/Field/FieldWidget => }/PathautoWidget.php (91%) diff --git a/pathauto.module b/pathauto.module index ce2d79d..7f485a6 100644 --- a/pathauto.module +++ b/pathauto.module @@ -206,18 +206,12 @@ function pathauto_field_info_alter(&$info) { } /** - * Implements hook_entity_base_field_info_alter(). - */ -function pathauto_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) { - if (isset($fields['path']) && $fields['path']->getType() == 'path') { - $fields['path']->setDisplayOptions('form', array( - 'type' => 'pathauto', - 'weight' => 30, - )); - } + * Implements hook_field_widget_info_alter(). + */ +function pathauto_field_widget_info_alter(&$widgets) { + $widgets['path']['class'] = 'Drupal\pathauto\PathautoWidget'; } - /** * Implements hook_entity_base_field_info(). */ @@ -230,7 +224,7 @@ function pathauto_entity_base_field_info(EntityTypeInterface $entity_type) { ->setLabel(t('URL alias')) ->setTranslatable(TRUE) ->setDisplayOptions('form', array( - 'type' => 'pathauto', + 'type' => 'path', 'weight' => 30, )) ->setDisplayConfigurable('form', TRUE); diff --git a/src/Plugin/Field/FieldWidget/PathautoWidget.php b/src/PathautoWidget.php similarity index 91% rename from src/Plugin/Field/FieldWidget/PathautoWidget.php rename to src/PathautoWidget.php index 4a1a967..4b36668 100644 --- a/src/Plugin/Field/FieldWidget/PathautoWidget.php +++ b/src/PathautoWidget.php @@ -1,26 +1,17 @@ Date: Tue, 20 Oct 2015 09:26:22 +0200 Subject: [PATCH 077/169] Generalize the batchUpdate() method, remove duplicate code --- pathauto.module | 76 ----------------- .../AliasType/EntityAliasTypeBase.php | 82 ++++++++++++++++++- .../pathauto/AliasType/ForumAliasType.php | 42 +--------- .../pathauto/AliasType/NodeAliasType.php | 42 ---------- .../AliasType/TaxonomyTermAliasType.php | 46 ----------- .../pathauto/AliasType/UserAliasType.php | 44 ---------- 6 files changed, 81 insertions(+), 251 deletions(-) diff --git a/pathauto.module b/pathauto.module index 7f485a6..5faa79e 100644 --- a/pathauto.module +++ b/pathauto.module @@ -16,15 +16,11 @@ * @ingroup pathauto */ - use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\Language\Language; use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\node\Entity\Node; -use Drupal\user\Entity\User; /** * The default ignore word list. @@ -126,78 +122,6 @@ function pathauto_entity_delete(EntityInterface $entity) { } } -/** - * Update the URL aliases for multiple nodes. - * - * @param array $nids - * An array of node IDs. - * @param string $op - * Operation being performed on the nodes ('insert', 'update' or - * 'bulkupdate'). - * @param array $options - * An optional array of additional options. - */ -function pathauto_node_update_alias_multiple(array $nids, $op, array $options = array()) { - $options += array('message' => FALSE); - - $nodes = Node::loadMultiple($nids); - foreach ($nodes as $node) { - \Drupal::service('pathauto.manager')->updateAlias($node, $op, $options); - } - - if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($nids), 'Updated URL alias for 1 node.', 'Updated URL aliases for @count nodes.')); - } -} - -/** - * Update the URL aliases for multiple taxonomy terms. - * - * @param array $tids - * An array of term IDs. - * @param string $op - * Operation being performed on the nodes ('insert', 'update' or - * 'bulkupdate'). - * @param array $options - * An optional array of additional options. - */ -function pathauto_taxonomy_term_update_alias_multiple(array $tids, $op, array $options = array()) { - $options += array('message' => FALSE); - - $terms = entity_load_multiple('taxonomy_term', $tids); - foreach ($terms as $term) { - \Drupal::service('pathauto.manager')->updateAlias($term, $op, $options); - } - - if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($tids), 'Updated URL alias for 1 term.', 'Updated URL aliases for @count terms.')); - } -} - -/** - * Update the URL aliases for multiple user accounts. - * - * @param array $uids - * An array of user account IDs. - * @param string $op - * Operation being performed on the accounts ('insert', 'update' or - * 'bulkupdate'). - * @param array $options - * An optional array of additional options. - */ -function pathauto_user_update_alias_multiple(array $uids, $op, array $options = array()) { - $options += array('message' => FALSE); - - $accounts = User::loadMultiple($uids); - foreach ($accounts as $account) { - \Drupal::service('pathauto.manager')->updateAlias($account, $op, $options); - } - - if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($uids), 'Updated URL alias for 1 user account.', 'Updated URL aliases for @count user accounts.')); - } -} - /** * Implements hook_field_info_alter(). */ diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 3bbc141..733b754 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -181,7 +181,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta public function getPatterns() { $patterns = []; $languages = $this->languageManager->getLanguages(); - if ($this->entityManager->getDefinition($this->getPluginId())->hasKey('bundle')) { + if ($this->entityManager->getDefinition($this->getEntityTypeId())->hasKey('bundle')) { foreach ($this->getBundles() as $bundle => $bundle_label) { if (count($languages) && $this->isContentTranslationEnabled($bundle)) { $patterns[$bundle] = $this->t('Default path pattern for @bundle (applies to all @bundle fields with blank patterns below)', array('@bundle' => $bundle_label)); @@ -200,6 +200,82 @@ public function getPatterns() { return $patterns; } + /** + * {@inheritdoc} + */ + public function batchUpdate(&$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + $entity_type = $this->entityManager->getDefinition($this->getEntityTypeId()); + $id_key = $entity_type->getKey('id'); + + $query = db_select($entity_type->get('base_table'), 'base_table'); + $query->leftJoin('url_alias', 'ua', "CONCAT('" . $this->getSourcePrefix() . "' , base_table.$id_key) = ua.source"); + $query->addField('base_table', $id_key, 'id'); + $query->isNull('ua.source'); + $query->condition('base_table.' . $id_key, $context['sandbox']['current'], '>'); + $query->orderBy('base_table.' . $id_key); + $query->addTag('pathauto_bulk_update'); + $query->addMetaData('entity', $this->getEntityTypeId()); + + // Get the total amount of items to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); + + // If there are no nodes to update, the stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + $query->range(0, 25); + $ids = $query->execute()->fetchCol(); + + $this->bulkUpdate($ids); + $context['sandbox']['count'] += count($ids); + $context['sandbox']['current'] = max($ids); + $context['message'] = t('Updated alias for %label @id.', array('%label' => $entity_type->getLabel(), '@id' => end($ids))); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } + } + + /** + * Returns the entity type ID. + * + * @return string + * The entity type ID. + */ + protected function getEntityTypeId() { + return $this->getPluginId(); + } + + /** + * Update the URL aliases for multiple entities. + * + * @param array $ids + * An array of entity IDs IDs. + * @param array $options + * An optional array of additional options. + */ + protected function bulkUpdate(array $ids, array $options = array()) { + $options += array('message' => FALSE); + + $entities = $this->entityManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); + foreach ($entities as $node) { + \Drupal::service('pathauto.manager')->updateAlias($node, 'bulkupdate', $options); + } + + if (!empty($options['message'])) { + drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated URL alias for 1 node.', 'Updated URL aliases for @count nodes.')); + } + } + /** * {@inheritdoc} */ @@ -227,7 +303,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s protected function getBundles() { return array_map(function ($bundle_info) { return $bundle_info['label']; - }, $this->entityManager->getBundleInfo($this->getPluginId())); + }, $this->entityManager->getBundleInfo($this->getEntityTypeId())); } /** @@ -240,7 +316,7 @@ protected function getBundles() { * TRUE if content translation is enabled for the bundle. */ protected function isContentTranslationEnabled($bundle) { - return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled($this->getPluginId(), $bundle); + return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled($this->getEntityTypeId(), $bundle); } } diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 155bf2b..8b88921 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -46,46 +46,8 @@ public function defaultConfiguration() { /** * {@inheritdoc} */ - public function batchUpdate(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('taxonomy_term_data', 'td'); - $query->leftJoin('url_alias', 'ua', "CONCAT('forum/', td.tid) = ua.source"); - $query->addField('td', 'tid'); - $query->isNull('ua.source'); - $query->condition('td.tid', $context['sandbox']['current'], '>'); - $query->condition('td.vid', 'forums'); - $query->orderBy('td.tid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'taxonomy_term'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery() - ->execute() - ->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $tids = $query->execute()->fetchCol(); - - pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); - $context['sandbox']['count'] += count($tids); - $context['sandbox']['current'] = max($tids); - $context['message'] = t('Updated alias for forum @tid.', array('@tid' => end($tids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } + protected function getEntityTypeId() { + return 'taxonomy_term'; } /** diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 86601e2..28859ea 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -36,48 +36,6 @@ public function defaultConfiguration() { return array('default' => array('/content/[node:title]')) + parent::defaultConfiguration(); } - /** - * {@inheritdoc} - */ - public function batchUpdate(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('node', 'n'); - $query->leftJoin('url_alias', 'ua', "CONCAT('node/', n.nid) = ua.source"); - $query->addField('n', 'nid'); - $query->isNull('ua.source'); - $query->condition('n.nid', $context['sandbox']['current'], '>'); - $query->orderBy('n.nid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'node'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $nids = $query->execute()->fetchCol(); - - pathauto_node_update_alias_multiple($nids, 'bulkupdate'); - $context['sandbox']['count'] += count($nids); - $context['sandbox']['current'] = max($nids); - $context['message'] = t('Updated alias for node @nid.', array('@nid' => end($nids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 4a0c24d..448d2e4 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -36,52 +36,6 @@ public function defaultConfiguration() { return array('default' => array('/[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); } - /** - * {@inheritdoc} - */ - public function batchUpdate(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('taxonomy_term_data', 'td'); - $query->leftJoin('url_alias', 'ua', "CONCAT('taxonomy/term/', td.tid) = ua.source"); - $query->addField('td', 'tid'); - $query->isNull('ua.source'); - $query->condition('td.tid', $context['sandbox']['current'], '>'); - // Exclude the forums terms. - if ($forum_vid = 'forums') { - $query->condition('td.vid', $forum_vid, '<>'); - } - $query->orderBy('td.tid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'taxonomy_term'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $tids = $query->execute()->fetchCol(); - - pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate'); - $context['sandbox']['count'] += count($tids); - $context['sandbox']['current'] = max($tids); - $context['message'] = t('Updated alias for term @tid.', array('@tid' => end($tids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index 0be986a..616d594 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -36,50 +36,6 @@ public function defaultConfiguration() { return array('default' => array('/users/[user:name]')) + parent::defaultConfiguration(); } - /** - * {@inheritdoc} - */ - public function batchUpdate(&$context) { - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['count'] = 0; - $context['sandbox']['current'] = 0; - } - - $query = db_select('users', 'u'); - $query->leftJoin('url_alias', 'ua', "CONCAT('/user/', u.uid) = ua.source"); - $query->addField('u', 'uid'); - $query->isNull('ua.source'); - $query->condition('u.uid', $context['sandbox']['current'], '>'); - $query->orderBy('u.uid'); - $query->addTag('pathauto_bulk_update'); - $query->addMetaData('entity', 'user'); - - // Get the total amount of items to process. - if (!isset($context['sandbox']['total'])) { - $context['sandbox']['total'] = $query->countQuery() - ->execute() - ->fetchField(); - - // If there are no nodes to update, the stop immediately. - if (!$context['sandbox']['total']) { - $context['finished'] = 1; - return; - } - } - - $query->range(0, 25); - $uids = $query->execute()->fetchCol(); - - pathauto_user_update_alias_multiple($uids, 'bulkupdate', array()); - $context['sandbox']['count'] += count($uids); - $context['sandbox']['current'] = max($uids); - $context['message'] = t('Updated alias for user @uid.', array('@uid' => end($uids))); - - if ($context['sandbox']['count'] != $context['sandbox']['total']) { - $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; - } - } - /** * {@inheritdoc} */ From f4336daaacd059243db349772de5e7388731f442 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 20 Oct 2015 08:48:08 +0200 Subject: [PATCH 078/169] Move cleanString() and related methods to AliasCleaner --- pathauto.services.yml | 4 +- pathauto.tokens.inc | 2 +- src/AliasCleaner.php | 251 +++++++++++++++++++++++++++++- src/AliasCleanerInterface.php | 61 ++++++++ src/Form/PathautoSettingsForm.php | 2 +- src/PathautoManager.php | 228 +-------------------------- src/PathautoManagerInterface.php | 56 ------- src/Tests/PathautoUnitTest.php | 4 +- 8 files changed, 321 insertions(+), 287 deletions(-) diff --git a/pathauto.services.yml b/pathauto.services.yml index c740b6b..dfd5cea 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -1,10 +1,10 @@ services: pathauto.manager: class: Drupal\pathauto\PathautoManager - arguments: ['@config.factory', '@language_manager', '@cache.default', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', ,'@pathauto.verbose_messenger', '@string_translation'] + arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', ,'@pathauto.verbose_messenger', '@string_translation'] pathauto.alias_cleaner: class: Drupal\pathauto\AliasCleaner - arguments: ['@config.factory', '@pathauto.alias_storage_helper'] + arguments: ['@config.factory', '@pathauto.alias_storage_helper', '@language_manager', '@cache.discovery', '@transliteration', '@module_handler'] pathauto.alias_storage_helper: class: Drupal\pathauto\AliasStorageHelper arguments: ['@config.factory', '@path.alias_storage', '@database','@pathauto.verbose_messenger', '@string_translation'] diff --git a/pathauto.tokens.inc b/pathauto.tokens.inc index 06c8c93..32a2b09 100644 --- a/pathauto.tokens.inc +++ b/pathauto.tokens.inc @@ -38,7 +38,7 @@ function pathauto_tokens($type, $tokens, array $data, array $options, Bubbleable $values = array(); foreach (token_element_children($array) as $key) { $value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key]; - $value = \Drupal::service('pathauto.manager')->cleanString($value, $options); + $value = \Drupal::service('pathauto.alias_cleaner')->cleanString($value, $options); $values[] = $value; } $replacements[$original] = implode('/', $values); diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index 3a185dd..889fc15 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -7,8 +7,13 @@ namespace Drupal\pathauto; +use Drupal\Component\Render\PlainTextOutput; +use Drupal\Component\Transliteration\TransliterationInterface; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Language\LanguageManagerInterface; /** * Provides an alias cleaner. @@ -29,6 +34,43 @@ class AliasCleaner implements AliasCleanerInterface { */ protected $aliasStorageHelper; + /** + * Language manager. + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + + /** + * Cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cacheBackend; + + /** + * Calculated settings cache. + * + * @todo Split this up into separate properties. + * + * @var array + */ + protected $cleanStringCache = array(); + + /** + * Transliteration service. + * + * @var \Drupal\Component\Transliteration\TransliterationInterface + */ + protected $transliteration; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * Creates a new AliasCleaner. * @@ -36,10 +78,22 @@ class AliasCleaner implements AliasCleanerInterface { * The config factory. * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper * The alias storage helper. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * The cache backend. + * @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration + * The transliteration service. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. */ - public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper) { + public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, LanguageManagerInterface $language_manager, CacheBackendInterface $cache_backend, TransliterationInterface $transliteration, ModuleHandlerInterface $module_handler) { $this->configFactory = $config_factory; $this->aliasStorageHelper = $alias_storage_helper; + $this->languageManager = $language_manager; + $this->cacheBackend = $cache_backend; + $this->transliteration = $transliteration; + $this->moduleHandler = $module_handler; } /** @@ -102,4 +156,199 @@ public function getCleanSeparators($string, $separator = NULL) { return $output; } + /** + * {@inheritdoc} + */ + public function cleanString($string, array $options = array()) { + if (empty($this->cleanStringCache)) { + // Generate and cache variables used in this method. + $config = $this->configFactory->get('pathauto.settings'); + $this->cleanStringCache = array( + 'separator' => $config->get('separator'), + 'strings' => array(), + 'transliterate' => $config->get('transliterate'), + 'punctuation' => array(), + 'reduce_ascii' => (bool) $config->get('reduce_ascii'), + 'ignore_words_regex' => FALSE, + 'lowercase' => (bool) $config->get('case'), + 'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()), + ); + + // Generate and cache the punctuation replacements for strtr(). + $punctuation = $this->getPunctuationCharacters(); + foreach ($punctuation as $name => $details) { + $action = $config->get('punctuation.' . $name); + switch ($action) { + case PathautoManagerInterface::PUNCTUATION_REMOVE: + $cache['punctuation'][$details['value']] = ''; + $this->cleanStringCache; + + case PathautoManagerInterface::PUNCTUATION_REPLACE: + $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator']; + break; + + case PathautoManagerInterface::PUNCTUATION_DO_NOTHING: + // Literally do nothing. + break; + } + } + + // Generate and cache the ignored words regular expression. + $ignore_words = $config->get('ignore_words'); + $ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words); + if ($ignore_words_regex) { + $this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b'; + if (function_exists('mb_eregi_replace')) { + mb_regex_encoding('UTF-8'); + $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace'; + } + else { + $this->cleanStringCache['ignore_words_callback'] = 'preg_replace'; + $this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i'; + } + } + } + + // Empty strings do not need any processing. + if ($string === '' || $string === NULL) { + return ''; + } + + $langcode = NULL; + if (!empty($options['language'])) { + $langcode = $options['language']->getId(); + } + elseif (!empty($options['langcode'])) { + $langcode = $options['langcode']; + } + + // Check if the string has already been processed, and if so return the + // cached result. + if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) { + return $this->cleanStringCache['strings'][$langcode][(string) $string]; + } + + // Remove all HTML tags from the string. + $output = PlainTextOutput::renderFromHtml($string); + + // Optionally transliterate. + if ($this->cleanStringCache['transliterate']) { + // If the reduce strings to letters and numbers is enabled, don't bother + // replacing unknown characters with a question mark. Use an empty string + // instead. + $output = $this->transliteration->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?'); + } + + // Replace or drop punctuation based on user settings. + $output = strtr($output, $this->cleanStringCache['punctuation']); + + // Reduce strings to letters and numbers. + if ($this->cleanStringCache['reduce_ascii']) { + $output = preg_replace('/[^a-zA-Z0-9\/]+/', $this->cleanStringCache['separator'], $output); + } + + // Get rid of words that are on the ignore list. + if ($this->cleanStringCache['ignore_words_regex']) { + $words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output); + if (Unicode::strlen(trim($words_removed)) > 0) { + $output = $words_removed; + } + } + + // Always replace whitespace with the separator. + $output = preg_replace('/\s+/', $this->cleanStringCache['separator'], $output); + + // Trim duplicates and remove trailing and leading separators. + $output = $this->getCleanSeparators($this->getCleanSeparators($output, $this->cleanStringCache['separator'])); + + // Optionally convert to lower case. + if ($this->cleanStringCache['lowercase']) { + $output = Unicode::strtolower($output); + } + + // Shorten to a logical place based on word boundaries. + $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE); + + // Cache this result in the static array. + $this->cleanStringCache['strings'][$langcode][(string) $string] = $output; + + return $output; + } + + + /** + * {@inheritdoc} + */ + public function getPunctuationCharacters() { + if (empty($this->punctuationCharacters)) { + $langcode = $this->languageManager->getCurrentLanguage()->getId(); + + $cid = 'pathauto:punctuation:' . $langcode; + if ($cache = $this->cacheBackend->get($cid)) { + $this->punctuationCharacters = $cache->data; + } + else { + $punctuation = array(); + $punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotation marks')); + $punctuation['quotes'] = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)")); + $punctuation['backtick'] = array('value' => '`', 'name' => t('Back tick')); + $punctuation['comma'] = array('value' => ',', 'name' => t('Comma')); + $punctuation['period'] = array('value' => '.', 'name' => t('Period')); + $punctuation['hyphen'] = array('value' => '-', 'name' => t('Hyphen')); + $punctuation['underscore'] = array('value' => '_', 'name' => t('Underscore')); + $punctuation['colon'] = array('value' => ':', 'name' => t('Colon')); + $punctuation['semicolon'] = array('value' => ';', 'name' => t('Semicolon')); + $punctuation['pipe'] = array('value' => '|', 'name' => t('Vertical bar (pipe)')); + $punctuation['left_curly'] = array('value' => '{', 'name' => t('Left curly bracket')); + $punctuation['left_square'] = array('value' => '[', 'name' => t('Left square bracket')); + $punctuation['right_curly'] = array('value' => '}', 'name' => t('Right curly bracket')); + $punctuation['right_square'] = array('value' => ']', 'name' => t('Right square bracket')); + $punctuation['plus'] = array('value' => '+', 'name' => t('Plus sign')); + $punctuation['equal'] = array('value' => '=', 'name' => t('Equal sign')); + $punctuation['asterisk'] = array('value' => '*', 'name' => t('Asterisk')); + $punctuation['ampersand'] = array('value' => '&', 'name' => t('Ampersand')); + $punctuation['percent'] = array('value' => '%', 'name' => t('Percent sign')); + $punctuation['caret'] = array('value' => '^', 'name' => t('Caret')); + $punctuation['dollar'] = array('value' => '$', 'name' => t('Dollar sign')); + $punctuation['hash'] = array('value' => '#', 'name' => t('Number sign (pound sign, hash)')); + $punctuation['at'] = array('value' => '@', 'name' => t('At sign')); + $punctuation['exclamation'] = array('value' => '!', 'name' => t('Exclamation mark')); + $punctuation['tilde'] = array('value' => '~', 'name' => t('Tilde')); + $punctuation['left_parenthesis'] = array('value' => '(', 'name' => t('Left parenthesis')); + $punctuation['right_parenthesis'] = array('value' => ')', 'name' => t('Right parenthesis')); + $punctuation['question_mark'] = array('value' => '?', 'name' => t('Question mark')); + $punctuation['less_than'] = array('value' => '<', 'name' => t('Less-than sign')); + $punctuation['greater_than'] = array('value' => '>', 'name' => t('Greater-than sign')); + $punctuation['slash'] = array('value' => '/', 'name' => t('Slash')); + $punctuation['back_slash'] = array('value' => '\\', 'name' => t('Backslash')); + + // Allow modules to alter the punctuation list and cache the result. + $this->moduleHandler->alter('pathauto_punctuation_chars', $punctuation); + $this->cacheBackend->set($cid, $punctuation); + $this->punctuationCharacters = $punctuation; + } + } + + return $this->punctuationCharacters; + } + + /** + * {@inheritdoc} + */ + public function cleanTokenValues(&$replacements, $data = array(), $options = array()) { + foreach ($replacements as $token => $value) { + // Only clean non-path tokens. + if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) { + $replacements[$token] = $this->cleanString($value, $options); + } + } + } + + /** + * {@inheritdoc} + */ + public function resetCaches() { + $this->cleanStringCache = array(); + } + } diff --git a/src/AliasCleanerInterface.php b/src/AliasCleanerInterface.php index 3ffed9a..7c90c1e 100644 --- a/src/AliasCleanerInterface.php +++ b/src/AliasCleanerInterface.php @@ -42,4 +42,65 @@ public function cleanAlias($alias); * @see pathauto_clean_alias() */ public function getCleanSeparators($string, $separator = NULL); + + /** + * Clean up a string segment to be used in an URL alias. + * + * Performs the following possible alterations: + * - Remove all HTML tags. + * - Process the string through the transliteration module. + * - Replace or remove punctuation with the separator character. + * - Remove back-slashes. + * - Replace non-ascii and non-numeric characters with the separator. + * - Remove common words. + * - Replace whitespace with the separator character. + * - Trim duplicate, leading, and trailing separators. + * - Convert to lower-case. + * - Shorten to a desired length and logical position based on word boundaries. + * + * This function should *not* be called on URL alias or path strings + * because it is assumed that they are already clean. + * + * @param string $string + * A string to clean. + * @param array $options + * (optional) A keyed array of settings and flags to control the Pathauto + * clean string replacement process. Supported options are: + * - langcode: A language code to be used when translating strings. + * + * @return string + * The cleaned string. + */ + public function cleanString($string, array $options = array()); + + /** + * Return an array of arrays for punctuation values. + * + * Returns an array of arrays for punctuation values keyed by a name, including + * the value and a textual description. + * Can and should be expanded to include "all" non text punctuation values. + * + * @return array + * An array of arrays for punctuation values keyed by a name, including the + * value and a textual description. + */ + public function getPunctuationCharacters(); + + /** + * Clean tokens so they are URL friendly. + * + * @param array $replacements + * An array of token replacements + * that need to be "cleaned" for use in the URL. + * @param array $data + * An array of objects used to generate the replacements. + * @param array $options + * An array of options used to generate the replacements. + */ + public function cleanTokenValues(&$replacements, $data = array(), $options = array()); + + /** + * Resets internal caches. + */ + public function resetCaches(); } diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index b8d8bcd..422bc9e 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -156,7 +156,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#tree' => TRUE, ); - $punctuation = \Drupal::service('pathauto.manager')->getPunctuationCharacters(); + $punctuation = \Drupal::service('pathauto.alias_cleaner')->getPunctuationCharacters(); foreach ($punctuation as $name => $details) { $details['default'] = PathautoManagerInterface::PUNCTUATION_REMOVE; diff --git a/src/PathautoManager.php b/src/PathautoManager.php index eed8f2d..9d5abf5 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -7,16 +7,12 @@ namespace Drupal\pathauto; -use Drupal\Component\Render\PlainTextOutput; -use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; @@ -29,15 +25,6 @@ class PathautoManager implements PathautoManagerInterface { use StringTranslationTrait; - /** - * Calculated settings cache. - * - * @todo Split this up into separate properties. - * - * @var array - */ - protected $cleanStringCache = array(); - /** * Punctuation characters cache. * @@ -52,19 +39,6 @@ class PathautoManager implements PathautoManagerInterface { */ protected $configFactory; - /** - * Language manager. - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * Cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface - */ - protected $cacheBackend; - /** * Module handler. * @@ -119,10 +93,6 @@ class PathautoManager implements PathautoManagerInterface { * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend - * The cache backend. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Utility\Token $token @@ -138,10 +108,8 @@ class PathautoManager implements PathautoManagerInterface { * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation service. */ - public function __construct(ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation) { $this->configFactory = $config_factory; - $this->languageManager = $language_manager; - $this->cacheBackend = $cache_backend; $this->moduleHandler = $module_handler; $this->token = $token; $this->aliasCleaner = $alias_cleaner; @@ -151,183 +119,6 @@ public function __construct(ConfigFactoryInterface $config_factory, LanguageMana $this->stringTranslation = $string_translation; } - /** - * {@inheritdoc} - */ - public function cleanString($string, array $options = array()) { - if (empty($this->cleanStringCache)) { - // Generate and cache variables used in this method. - $config = $this->configFactory->get('pathauto.settings'); - $this->cleanStringCache = array( - 'separator' => $config->get('separator'), - 'strings' => array(), - 'transliterate' => $config->get('transliterate'), - 'punctuation' => array(), - 'reduce_ascii' => (bool) $config->get('reduce_ascii'), - 'ignore_words_regex' => FALSE, - 'lowercase' => (bool) $config->get('case'), - 'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()), - ); - - // Generate and cache the punctuation replacements for strtr(). - $punctuation = $this->getPunctuationCharacters(); - foreach ($punctuation as $name => $details) { - $action = $config->get('punctuation.' . $name); - switch ($action) { - case PathautoManagerInterface::PUNCTUATION_REMOVE: - $cache['punctuation'][$details['value']] = ''; - $this->cleanStringCache; - - case PathautoManagerInterface::PUNCTUATION_REPLACE: - $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator']; - break; - - case PathautoManagerInterface::PUNCTUATION_DO_NOTHING: - // Literally do nothing. - break; - } - } - - // Generate and cache the ignored words regular expression. - $ignore_words = $config->get('ignore_words'); - $ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words); - if ($ignore_words_regex) { - $this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b'; - if (function_exists('mb_eregi_replace')) { - mb_regex_encoding('UTF-8'); - $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace'; - } - else { - $this->cleanStringCache['ignore_words_callback'] = 'preg_replace'; - $this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i'; - } - } - } - - // Empty strings do not need any processing. - if ($string === '' || $string === NULL) { - return ''; - } - - $langcode = NULL; - if (!empty($options['language'])) { - $langcode = $options['language']->getId(); - } - elseif (!empty($options['langcode'])) { - $langcode = $options['langcode']; - } - - // Check if the string has already been processed, and if so return the - // cached result. - if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) { - return $this->cleanStringCache['strings'][$langcode][(string) $string]; - } - - // Remove all HTML tags from the string. - $output = PlainTextOutput::renderFromHtml($string); - - // Optionally transliterate. - if ($this->cleanStringCache['transliterate']) { - // If the reduce strings to letters and numbers is enabled, don't bother - // replacing unknown characters with a question mark. Use an empty string - // instead. - $output = \Drupal::service('transliteration')->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?'); - } - - // Replace or drop punctuation based on user settings. - $output = strtr($output, $this->cleanStringCache['punctuation']); - - // Reduce strings to letters and numbers. - if ($this->cleanStringCache['reduce_ascii']) { - $output = preg_replace('/[^a-zA-Z0-9\/]+/', $this->cleanStringCache['separator'], $output); - } - - // Get rid of words that are on the ignore list. - if ($this->cleanStringCache['ignore_words_regex']) { - $words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output); - if (Unicode::strlen(trim($words_removed)) > 0) { - $output = $words_removed; - } - } - - // Always replace whitespace with the separator. - $output = preg_replace('/\s+/', $this->cleanStringCache['separator'], $output); - - // Trim duplicates and remove trailing and leading separators. - $output = $this->aliasCleaner->getCleanSeparators($this->aliasCleaner->getCleanSeparators($output, $this->cleanStringCache['separator'])); - - // Optionally convert to lower case. - if ($this->cleanStringCache['lowercase']) { - $output = Unicode::strtolower($output); - } - - // Shorten to a logical place based on word boundaries. - $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE); - - // Cache this result in the static array. - $this->cleanStringCache['strings'][$langcode][(string) $string] = $output; - - return $output; - } - - - /** - * {@inheritdoc} - */ - public function getPunctuationCharacters() { - if (empty($this->punctuationCharacters)) { - $langcode = $this->languageManager->getCurrentLanguage()->getId(); - - $cid = 'pathauto:punctuation:' . $langcode; - if ($cache = $this->cacheBackend->get($cid)) { - $this->punctuationCharacters = $cache->data; - } - else { - $punctuation = array(); - $punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotation marks')); - $punctuation['quotes'] = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)")); - $punctuation['backtick'] = array('value' => '`', 'name' => t('Back tick')); - $punctuation['comma'] = array('value' => ',', 'name' => t('Comma')); - $punctuation['period'] = array('value' => '.', 'name' => t('Period')); - $punctuation['hyphen'] = array('value' => '-', 'name' => t('Hyphen')); - $punctuation['underscore'] = array('value' => '_', 'name' => t('Underscore')); - $punctuation['colon'] = array('value' => ':', 'name' => t('Colon')); - $punctuation['semicolon'] = array('value' => ';', 'name' => t('Semicolon')); - $punctuation['pipe'] = array('value' => '|', 'name' => t('Vertical bar (pipe)')); - $punctuation['left_curly'] = array('value' => '{', 'name' => t('Left curly bracket')); - $punctuation['left_square'] = array('value' => '[', 'name' => t('Left square bracket')); - $punctuation['right_curly'] = array('value' => '}', 'name' => t('Right curly bracket')); - $punctuation['right_square'] = array('value' => ']', 'name' => t('Right square bracket')); - $punctuation['plus'] = array('value' => '+', 'name' => t('Plus sign')); - $punctuation['equal'] = array('value' => '=', 'name' => t('Equal sign')); - $punctuation['asterisk'] = array('value' => '*', 'name' => t('Asterisk')); - $punctuation['ampersand'] = array('value' => '&', 'name' => t('Ampersand')); - $punctuation['percent'] = array('value' => '%', 'name' => t('Percent sign')); - $punctuation['caret'] = array('value' => '^', 'name' => t('Caret')); - $punctuation['dollar'] = array('value' => '$', 'name' => t('Dollar sign')); - $punctuation['hash'] = array('value' => '#', 'name' => t('Number sign (pound sign, hash)')); - $punctuation['at'] = array('value' => '@', 'name' => t('At sign')); - $punctuation['exclamation'] = array('value' => '!', 'name' => t('Exclamation mark')); - $punctuation['tilde'] = array('value' => '~', 'name' => t('Tilde')); - $punctuation['left_parenthesis'] = array('value' => '(', 'name' => t('Left parenthesis')); - $punctuation['right_parenthesis'] = array('value' => ')', 'name' => t('Right parenthesis')); - $punctuation['question_mark'] = array('value' => '?', 'name' => t('Question mark')); - $punctuation['less_than'] = array('value' => '<', 'name' => t('Less-than sign')); - $punctuation['greater_than'] = array('value' => '>', 'name' => t('Greater-than sign')); - $punctuation['slash'] = array('value' => '/', 'name' => t('Slash')); - $punctuation['back_slash'] = array('value' => '\\', 'name' => t('Backslash')); - - // Allow modules to alter the punctuation list and cache the result. - $this->moduleHandler->alter('pathauto_punctuation_chars', $punctuation); - $this->cacheBackend->set($cid, $punctuation); - $this->punctuationCharacters = $punctuation; - } - } - - return $this->punctuationCharacters; - } - - /** * {@inheritdoc} */ @@ -373,7 +164,7 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod // as the result is never rendered. $alias = $this->token->replace($pattern, $data, array( 'clear' => TRUE, - 'callback' => array($this, 'cleanTokenValues'), + 'callback' => array($this->aliasCleaner, 'cleanTokenValues'), 'langcode' => $langcode, 'pathauto' => TRUE, ), new BubbleableMetadata()); @@ -455,11 +246,11 @@ public function getPatternByEntity($entity_type_id, $bundle = '', $language = La } /** - * Resets internal caches. + * {@inheritdoc} */ public function resetCaches() { $this->patterns = array(); - $this->cleanStringCache = array(); + $this->aliasCleaner->resetCaches(); } /** @@ -535,15 +326,4 @@ protected function getTermTree($vid, $parent = 0, $max_depth = NULL, $load_entit return \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities); } - /** - * {@inheritdoc} - */ - public function cleanTokenValues(&$replacements, $data = array(), $options = array()) { - foreach ($replacements as $token => $value) { - // Only clean non-path tokens. - if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) { - $replacements[$token] = $this->cleanString($value, $options); - } - } - } } diff --git a/src/PathautoManagerInterface.php b/src/PathautoManagerInterface.php index 9d56abd..f806b8a 100644 --- a/src/PathautoManagerInterface.php +++ b/src/PathautoManagerInterface.php @@ -49,36 +49,6 @@ interface PathautoManagerInterface { */ public function resetCaches(); - /** - * Clean up a string segment to be used in an URL alias. - * - * Performs the following possible alterations: - * - Remove all HTML tags. - * - Process the string through the transliteration module. - * - Replace or remove punctuation with the separator character. - * - Remove back-slashes. - * - Replace non-ascii and non-numeric characters with the separator. - * - Remove common words. - * - Replace whitespace with the separator character. - * - Trim duplicate, leading, and trailing separators. - * - Convert to lower-case. - * - Shorten to a desired length and logical position based on word boundaries. - * - * This function should *not* be called on URL alias or path strings - * because it is assumed that they are already clean. - * - * @param string $string - * A string to clean. - * @param array $options - * (optional) A keyed array of settings and flags to control the Pathauto - * clean string replacement process. Supported options are: - * - langcode: A language code to be used when translating strings. - * - * @return string - * The cleaned string. - */ - public function cleanString($string, array $options = array()); - /** * Load an URL alias pattern by entity, bundle, and language. * @@ -121,19 +91,6 @@ public function getPatternByEntity($entity_type_id, $bundle = '', $language = La */ public function createAlias($module, $op, $source, $data, $type = NULL, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED); - /** - * Return an array of arrays for punctuation values. - * - * Returns an array of arrays for punctuation values keyed by a name, including - * the value and a textual description. - * Can and should be expanded to include "all" non text punctuation values. - * - * @return array - * An array of arrays for punctuation values keyed by a name, including the - * value and a textual description. - */ - public function getPunctuationCharacters(); - /** * Creates or updates an alias for the given entity. * @@ -151,17 +108,4 @@ public function getPunctuationCharacters(); */ public function updateAlias(EntityInterface $entity, $op, array $options = array()); - /** - * Clean tokens so they are URL friendly. - * - * @param array $replacements - * An array of token replacements - * that need to be "cleaned" for use in the URL. - * @param array $data - * An array of objects used to generate the replacements. - * @param array $options - * An array of options used to generate the replacements. - */ - public function cleanTokenValues(&$replacements, $data = array(), $options = array()); - } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 7e9259e..5d4497f 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -145,8 +145,8 @@ public function testCleanString() { $tests['ľščťžýáíéňô'] = 'lsctzyaieno'; foreach ($tests as $input => $expected) { - $output = \Drupal::service('pathauto.manager')->cleanString($input); - $this->assertEqual($output, $expected, t("Drupal::service('pathauto.manager')->cleanString('@input') expected '@expected', actual '@output'", array( + $output = \Drupal::service('pathauto.alias_cleaner')->cleanString($input); + $this->assertEqual($output, $expected, t("Drupal::service('pathauto.alias_cleaner')->cleanString('@input') expected '@expected', actual '@output'", array( '@input' => $input, '@expected' => $expected, '@output' => $output, From a29d4c64cade4332d41e7862e066d849b47a70d5 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 20 Oct 2015 20:20:51 +0200 Subject: [PATCH 079/169] Update call to cleanTokenValues() in tests --- src/Tests/PathautoTokenTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tests/PathautoTokenTest.php b/src/Tests/PathautoTokenTest.php index 8c298e5..2ae50c9 100644 --- a/src/Tests/PathautoTokenTest.php +++ b/src/Tests/PathautoTokenTest.php @@ -40,9 +40,9 @@ public function testPathautoTokens() { $replacements = $this->assertTokens('array', $data, $tokens); // Ensure that the cleanTokenValues() method does not alter this token value. - /* @var \Drupal\pathauto\PathautoManagerInterface $manager */ - $manager = \Drupal::service('pathauto.manager'); - $manager->cleanTokenValues($replacements, $data, array()); + /* @var \Drupal\pathauto\AliasCleanerInterface $alias_cleaner */ + $alias_cleaner = \Drupal::service('pathauto.alias_cleaner'); + $alias_cleaner->cleanTokenValues($replacements, $data, array()); $this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value'); } From 7dc734e7a3fcb1950bbc0e1d7d5690e419eaafb0 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 20 Oct 2015 23:59:54 -0500 Subject: [PATCH 080/169] updating routes and menu entries as well as providing an initial start on a configuration wizard. --- pathauto.info.yml | 5 +- pathauto.links.action.yml | 6 ++ pathauto.links.menu.yml | 7 ++ pathauto.links.task.yml | 2 +- pathauto.routing.yml | 59 ++++++++++++- src/Entity/PathautoPattern.php | 12 +-- src/Form/ConfigurePatternForm.php | 51 ++++++++++++ src/Form/CriteriaDelete.php | 53 ++++++++++++ src/Form/CriteriaForm.php | 71 ++++++++++++++++ src/Form/PathautoPatternDeleteForm.php | 57 +++++++++++++ src/Form/PathautoPatternForm.php | 82 +++++++++++++++++++ src/Form/SelectionCriteriaForm.php | 81 ++++++++++++++++++ src/PathautoPatternListBuilder.php | 36 ++++++++ .../Field/FieldWidget/PathautoWidget.php | 2 +- src/Wizard/PatternWizard.php | 65 +++++++++++++++ src/Wizard/PatternWizardAdd.php | 21 +++++ 16 files changed, 595 insertions(+), 15 deletions(-) create mode 100644 pathauto.links.action.yml create mode 100644 pathauto.links.menu.yml create mode 100644 src/Form/ConfigurePatternForm.php create mode 100644 src/Form/CriteriaDelete.php create mode 100644 src/Form/CriteriaForm.php create mode 100644 src/Form/PathautoPatternDeleteForm.php create mode 100644 src/Form/PathautoPatternForm.php create mode 100644 src/Form/SelectionCriteriaForm.php create mode 100644 src/PathautoPatternListBuilder.php create mode 100644 src/Wizard/PatternWizard.php create mode 100644 src/Wizard/PatternWizardAdd.php diff --git a/pathauto.info.yml b/pathauto.info.yml index 2532cbb..8346881 100644 --- a/pathauto.info.yml +++ b/pathauto.info.yml @@ -4,10 +4,11 @@ core: 8.x type: module dependencies: +- ctools - path - token -configure: pathauto.patterns.form +configure: entity.pathauto_pattern.collection recommends: -- redirect \ No newline at end of file +- redirect diff --git a/pathauto.links.action.yml b/pathauto.links.action.yml new file mode 100644 index 0000000..d68b94a --- /dev/null +++ b/pathauto.links.action.yml @@ -0,0 +1,6 @@ +entity.pathauto_pattern.add_form: + route_name: 'entity.pathauto_pattern.add_form' + title: 'Add Pathauto pattern' + appears_on: + - entity.pathauto_pattern.collection + diff --git a/pathauto.links.menu.yml b/pathauto.links.menu.yml new file mode 100644 index 0000000..bec5554 --- /dev/null +++ b/pathauto.links.menu.yml @@ -0,0 +1,7 @@ +# Pathauto pattern menu items definition +entity.pathauto_pattern.collection: + title: 'Pathauto pattern' + route_name: entity.pathauto_pattern.collection + description: 'List Pathauto pattern' + parent: system.admin_structure + diff --git a/pathauto.links.task.yml b/pathauto.links.task.yml index 2ce9963..d6fa0bc 100644 --- a/pathauto.links.task.yml +++ b/pathauto.links.task.yml @@ -1,5 +1,5 @@ pathauto.patterns.form: - route_name: pathauto.patterns.form + route_name: entity.pathauto_pattern.collection base_route: path.admin_overview title: 'Patterns' weight: 10 diff --git a/pathauto.routing.yml b/pathauto.routing.yml index ecc5a7b..dbc4915 100644 --- a/pathauto.routing.yml +++ b/pathauto.routing.yml @@ -1,11 +1,64 @@ -pathauto.patterns.form: +entity.pathauto_pattern.collection: path: '/admin/config/search/path/patterns' defaults: - _form: '\Drupal\pathauto\Form\PathautoPatternsForm' + _entity_list: 'pathauto_pattern' _title: 'Patterns' requirements: _permission: 'administer pathauto' +entity.pathauto_pattern.add_form: + path: '/admin/config/search/path/patterns/add' + defaults: + _entity_wizard: 'pathauto_pattern.add' + _title: 'Add Pathauto pattern' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +entity.pathauto_pattern.edit_form: + path: '/admin/config/search/path/patterns/{machine_name}/{step}' + defaults: + _entity_wizard: 'pathauto_pattern.edit' + _title: 'Edit Pathauto pattern' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +entity.pathauto_pattern.delete_form: + path: '/admin/config/search/path/patterns/{pathauto_pattern}/delete' + defaults: + _entity_form: 'pathauto_pattern.delete' + _title: 'Delete Pathauto pattern' + requirements: + _permission: 'administer pathauto' + +pathauto.pattern.condition.add: + path: '/admin/config/search/path/patterns/{machine_name}/criteria/{condition}/add' + defaults: + _form: '\Drupal\pathauto\Form\CriteriaForm' + _title: 'Configure selection criteria' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +pathauto.pattern.condition.edit: + path: '/admin/config/search/path/patterns/{machine_name}/criteria/{condition}/edit' + defaults: + _form: '\Drupal\pathauto\Form\CriteriaForm' + _title: 'Configure selection criteria' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +pathauto.pattern.condition.delete: + path: '/admin/config/search/path/patterns/{machine_name}/criteria/{id}/delete' + defaults: + _form: '\Drupal\pathauto\Form\CriteriaDelete' + _title: 'Delete selection criteria' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + pathauto.settings.form: path: '/admin/config/search/path/settings' defaults: @@ -28,4 +81,4 @@ pathauto.admin.delete: _form: '\Drupal\pathauto\Form\PathautoAdminDelete' _title: 'Delete aliases' requirements: - _permission: 'administer url aliases' \ No newline at end of file + _permission: 'administer url aliases' diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index c7c564e..f80b27a 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -21,9 +21,11 @@ * handlers = { * "list_builder" = "Drupal\pathauto\PathautoPatternListBuilder", * "form" = { - * "add" = "Drupal\pathauto\Form\PathautoPatternForm", - * "edit" = "Drupal\pathauto\Form\PathautoPatternForm", * "delete" = "Drupal\pathauto\Form\PathautoPatternDeleteForm" + * }, + * "wizard" = { + * "add" = "Drupal\pathauto\Wizard\PatternWizardAdd", + * "edit" = "Drupal\pathauto\Wizard\PatternWizard" * } * }, * config_prefix = "pathauto_pattern", @@ -32,12 +34,6 @@ * "id" = "id", * "label" = "label", * "uuid" = "uuid" - * }, - * links = { - * "canonical" = "/admin/structure/pathauto_pattern/{pathauto_pattern}", - * "edit-form" = "/admin/structure/pathauto_pattern/{pathauto_pattern}/edit", - * "delete-form" = "/admin/structure/pathauto_pattern/{pathauto_pattern}/delete", - * "collection" = "/admin/structure/visibility_group" * } * ) */ diff --git a/src/Form/ConfigurePatternForm.php b/src/Form/ConfigurePatternForm.php new file mode 100644 index 0000000..0da2b05 --- /dev/null +++ b/src/Form/ConfigurePatternForm.php @@ -0,0 +1,51 @@ + $this->machine_name, 'step' => 'selection_criteria']]; + } + + protected function getConditions($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + $conditions = []; + foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { + $conditions[$uuid] = $configuration->getConfiguration(); + } + return $conditions; + } + + protected function setConditions($cached_values, $conditions) { + $old_conditions = $this->getConditions($cached_values); + $diff = array_diff(array_keys($old_conditions), array_keys($conditions)); + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + // There should only be one item in $diff, but we'll loop anyway. + foreach ($diff as $key) { + $pattern->removeSelectionCondition($key); + } + return $cached_values; + } + + protected function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + // @todo This is a total hack. The plugin that getType() represents should + // be responsible for this. + $type = $pattern->getType(); + $context_definition = new ContextDefinition('entity:' . $type); + return [ + $type => new Context($context_definition), + ]; + } + +} diff --git a/src/Form/CriteriaForm.php b/src/Form/CriteriaForm.php new file mode 100644 index 0000000..d74736e --- /dev/null +++ b/src/Form/CriteriaForm.php @@ -0,0 +1,71 @@ + $this->machine_name, 'step' => 'selection_criteria']]; + } + + /** + * {@inheritdoc} + */ + protected function getRouteInfo($condition) { + return ['pathauto.pattern.condition.add', ['machine_name' => $this->machine_name, 'condition' => $condition]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + $conditions = []; + foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { + $conditions[$uuid] = $configuration->getConfiguration(); + } + return $conditions; + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + // Set all the old conditions again since this is kind of indiscriminate. + foreach ($conditions as $id => $configuration) { + $pattern->getSelectionConditions()->setInstanceConfiguration($id, $conditions[$id]); + } + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + // @todo This is a total hack. The plugin that getType() represents should + // be responsible for this. + $type = $pattern->getType(); + $context_definition = new ContextDefinition('entity:' . $type); + return [ + $type => new Context($context_definition), + ]; + } + +} \ No newline at end of file diff --git a/src/Form/PathautoPatternDeleteForm.php b/src/Form/PathautoPatternDeleteForm.php new file mode 100644 index 0000000..24a6ee8 --- /dev/null +++ b/src/Form/PathautoPatternDeleteForm.php @@ -0,0 +1,57 @@ +t('Are you sure you want to delete %name?', array('%name' => $this->entity->label())); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.pathauto_pattern.collection'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->entity->delete(); + + drupal_set_message( + $this->t('content @type: deleted @label.', + [ + '@type' => $this->entity->bundle(), + '@label' => $this->entity->label() + ] + ) + ); + + $form_state->setRedirectUrl($this->getCancelUrl()); + } + +} diff --git a/src/Form/PathautoPatternForm.php b/src/Form/PathautoPatternForm.php new file mode 100644 index 0000000..6dd8bf3 --- /dev/null +++ b/src/Form/PathautoPatternForm.php @@ -0,0 +1,82 @@ +get('plugin.manager.alias_type')); + } + + /** + * @param \Drupal\pathauto\AliasTypeManager $manager + */ + function __construct(AliasTypeManager $manager) { + $this->manager = $manager; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'pathauto_pattern_general_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ + $pathauto_pattern = $cached_values['pathauto_pattern']; + $options = []; + foreach ($this->manager->getDefinitions() as $plugin_id => $plugin_definition) { + $options[$plugin_id] = (string) $plugin_definition['label']; + } + $form['type'] = [ + '#type' => 'select', + '#title' => $this->t('Pattern type'), + '#default_value' => $pathauto_pattern->getType(), + '#options' => $options, + '#required' => TRUE, + ]; + + return $form; + } + + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ + $pathauto_pattern = $cached_values['pathauto_pattern']; + $pathauto_pattern->set('label', $form_state->getValue('label')); + $pathauto_pattern->set('id', $form_state->getValue('id')); + $pathauto_pattern->set('type', $form_state->getValue('type')); + } + + +} diff --git a/src/Form/SelectionCriteriaForm.php b/src/Form/SelectionCriteriaForm.php new file mode 100644 index 0000000..0e9f5e6 --- /dev/null +++ b/src/Form/SelectionCriteriaForm.php @@ -0,0 +1,81 @@ + $machine_name, 'condition' => $row]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + $conditions = []; + foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { + $conditions[$uuid] = $configuration->getConfiguration(); + } + return $conditions; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + // @todo This is a total hack. The plugin that getType() represents should + // be responsible for this. + $type = $pattern->getType(); + $context_definition = new ContextDefinition('entity:' . $type); + return [ + $type => new Context($context_definition), + ]; + } + +} diff --git a/src/PathautoPatternListBuilder.php b/src/PathautoPatternListBuilder.php new file mode 100644 index 0000000..9c2c8d6 --- /dev/null +++ b/src/PathautoPatternListBuilder.php @@ -0,0 +1,36 @@ +t('Pathauto pattern'); + $header['id'] = $this->t('Machine name'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + $row['label'] = $this->getLabel($entity); + $row['id'] = $entity->id(); + // You probably want a few more properties here... + return $row + parent::buildRow($entity); + } + +} diff --git a/src/Plugin/Field/FieldWidget/PathautoWidget.php b/src/Plugin/Field/FieldWidget/PathautoWidget.php index 4a1a967..321ae73 100644 --- a/src/Plugin/Field/FieldWidget/PathautoWidget.php +++ b/src/Plugin/Field/FieldWidget/PathautoWidget.php @@ -73,7 +73,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#type' => 'checkbox', '#title' => $this->t('Generate automatic URL alias'), '#default_value' => $entity->path->pathauto, - '#description' => $this->t('Uncheck this to create a custom alias below. Configure URL alias patterns.', array('@admin_link' => \Drupal::url('pathauto.patterns.form'))), + '#description' => $this->t('Uncheck this to create a custom alias below. Configure URL alias patterns.', array('@admin_link' => \Drupal::url('entity.pathauto_pattern.collection'))), '#weight' => -1, ); diff --git a/src/Wizard/PatternWizard.php b/src/Wizard/PatternWizard.php new file mode 100644 index 0000000..e4c1fe9 --- /dev/null +++ b/src/Wizard/PatternWizard.php @@ -0,0 +1,65 @@ +t('Pattern'); + } + + /** + * {@inheritdoc} + */ + public function getMachineLabel() { + return $this->t('Identifier'); + } + + /** + * {@inheritdoc} + */ + public function getEntityType() { + return 'pathauto_pattern'; + } + + /** + * {@inheritdoc} + */ + public function exists() { + return 'Drupal\pathauto\Entity\PathautoPattern::load'; + } + + /** + * {@inheritdoc} + */ + public function getOperations() { + return [ + 'general' => [ + 'title' => $this->t('General information'), + 'form' => '\Drupal\pathauto\Form\PathautoPatternForm' + ], + 'selection_criteria' => [ + 'title' => $this->t('Selection criteria'), + 'form' => '\Drupal\pathauto\Form\SelectionCriteriaForm' + ], + 'pattern' => [ + 'title' => $this->t('Configure pattern'), + 'form' => '\Drupal\pathauto\Form\ConfigurePatternForm' + ] + ]; + } + +} \ No newline at end of file diff --git a/src/Wizard/PatternWizardAdd.php b/src/Wizard/PatternWizardAdd.php new file mode 100644 index 0000000..d7090bd --- /dev/null +++ b/src/Wizard/PatternWizardAdd.php @@ -0,0 +1,21 @@ + Date: Wed, 21 Oct 2015 10:29:16 -0500 Subject: [PATCH 081/169] initial addition of "applies" logic to AliasTypeInterface and corresponding class --- src/AliasTypeInterface.php | 10 ++++++++++ src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index 3fbf7a4..4cf6de8 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -55,4 +55,14 @@ public function getTokenTypes(); */ public function getSourcePrefix(); + /** + * Determines if this plugin type can apply a given object. + * + * @param $object + * The object used to determine if this plugin can apply. + * + * @return bool + */ + public function applies($object); + } diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 3bbc141..7cdd951 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -243,4 +243,15 @@ protected function isContentTranslationEnabled($bundle) { return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled($this->getPluginId(), $bundle); } + /** + * {@inheritdoc} + */ + public function applies($object) { + $definition = $this->entityManager->getDefinition($this->getDerivativeId()); + $class = $definition->getClass(); + if ($object instanceof $class) { + return TRUE; + } + return FALSE; + } } From 1ca8fdd3a7b5060c99d053e9a139083303cc7eae Mon Sep 17 00:00:00 2001 From: Kris Vanderwater Date: Mon, 26 Oct 2015 13:56:30 -0700 Subject: [PATCH 082/169] massive update that fully enables the wizard process and switches the administrative pages over to it. --- src/AliasTypeInterface.php | 11 +- src/AliasTypeManager.php | 15 +++ src/Entity/PathautoPattern.php | 58 ++++++++++ src/Form/ConfigurePatternForm.php | 26 ++--- src/Form/CriteriaDelete.php | 21 ++-- src/Form/CriteriaForm.php | 8 +- src/Form/SelectionCriteriaForm.php | 14 +-- src/PathautoManager.php | 85 ++++++++------- src/PathautoManagerInterface.php | 31 ++---- src/PathautoPatternInterface.php | 15 +++ src/PathautoPatternListBuilder.php | 11 ++ src/Plugin/Deriver/EntityAliasTypeDeriver.php | 27 +++++ .../AliasType/EntityAliasTypeBase.php | 101 ++++++++---------- .../pathauto/AliasType/ForumAliasType.php | 12 +++ .../AliasType/TaxonomyTermAliasType.php | 12 +++ src/Wizard/PatternWizard.php | 11 +- 16 files changed, 287 insertions(+), 171 deletions(-) create mode 100644 src/Plugin/Deriver/EntityAliasTypeDeriver.php diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index 4cf6de8..32bac49 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -8,12 +8,13 @@ namespace Drupal\pathauto; use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\PluginFormInterface; /** * Provides an interface for pathauto alias types. */ -interface AliasTypeInterface extends ConfigurablePluginInterface, PluginFormInterface { +interface AliasTypeInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface, PluginFormInterface { /** * Get the label. @@ -31,14 +32,6 @@ public function getLabel(); */ public function getPatternDescription(); - /** - * Get the patterns. - * - * @return string[] - * The array of patterns. - */ - public function getPatterns(); - /** * Get the token types. * diff --git a/src/AliasTypeManager.php b/src/AliasTypeManager.php index fa6b98f..fc54f69 100644 --- a/src/AliasTypeManager.php +++ b/src/AliasTypeManager.php @@ -33,4 +33,19 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac $this->setCacheBackend($cache_backend, 'pathauto_alias_types'); } + /** + * @param string $type + * The type of token plugin must support to be useful. + * @return array + */ + public function getPluginDefinitionByType($type) { + $definitions = array_filter($this->getDefinitions(), function ($definition) use ($type) { + if (!empty($definition['types']) && in_array($type, $definition['types'])) { + return TRUE; + } + return FALSE; + }); + return $definitions; + } + } diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index f80b27a..bd10973 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -7,9 +7,11 @@ namespace Drupal\pathauto\Entity; +use Drupal\Component\Plugin\ContextAwarePluginInterface; use Drupal\Core\Condition\ConditionPluginCollection; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; use Drupal\pathauto\PathautoPatternInterface; /** @@ -34,6 +36,11 @@ * "id" = "id", * "label" = "label", * "uuid" = "uuid" + * }, + * links = { + * "collection" = "/admin/config/search/path/patterns", + * "edit-form" = "/admin/config/search/path/patterns/{machine_name}/{step}", + * "delete-form" = "/admin/config/search/path/patterns/{pathauto_pattern}/delete" * } * ) */ @@ -63,6 +70,11 @@ class PathautoPattern extends ConfigEntityBase implements PathautoPatternInterfa */ protected $type; + /** + * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection + */ + protected $aliasTypeCollection; + /** * A tokenized string for alias generation. * @@ -117,6 +129,8 @@ public function preSave(EntityStorageInterface $storage) { public function calculateDependencies() { parent::calculateDependencies(); + // @todo get dependencies from the alias type plugin. + foreach ($this->getSelectionConditions() as $instance) { $this->calculatePluginDependencies($instance); } @@ -136,6 +150,7 @@ public function getPattern() { */ public function setPattern($pattern) { $this->pattern = $pattern; + $this->getAliasType()->setConfiguration(['default' => $pattern]); } /** @@ -145,6 +160,16 @@ public function getType() { return $this->type; } + /** + * {@inheritdoc} + */ + public function getAliasType() { + if (!$this->aliasTypeCollection) { + $this->aliasTypeCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.alias_type'), $this->getType(), ['default' => $this->getPattern()]); + } + return $this->aliasTypeCollection->get($this->getType()); + } + /** * {@inheritdoc} */ @@ -201,4 +226,37 @@ public function getSelectionLogic() { return $this->selection_logic; } + /** + * {@inheritdoc} + */ + public function applies($object) { + if ($this->getAliasType()->applies($object)) { + $definitions = $this->getAliasType()->getContextDefinitions(); + if (count($definitions) > 1) { + throw new \Exception("Alias types do not support more than one context."); + } + $keys = array_keys($definitions); + // Set the context object on our Alias plugin before retrieving contexts. + $this->getAliasType()->setContextValue($keys[0], $object); + $contexts = $this->getAliasType()->getContexts(); + /** @var \Drupal\Core\Plugin\Context\ContextHandler $context_handler */ + $context_handler = \Drupal::service('context.handler'); + $conditions = $this->getSelectionConditions(); + foreach ($conditions as $condition) { + if ($condition instanceof ContextAwarePluginInterface) { + $context_handler->applyContextMapping($condition, $contexts); + } + $result = $condition->execute(); + if ($this->getSelectionLogic() == 'and' && !$result) { + return FALSE; + } + elseif ($this->getSelectionLogic() == 'or' && $result) { + return TRUE; + } + } + return TRUE; + } + return FALSE; + } + } diff --git a/src/Form/ConfigurePatternForm.php b/src/Form/ConfigurePatternForm.php index 0da2b05..24225db 100644 --- a/src/Form/ConfigurePatternForm.php +++ b/src/Form/ConfigurePatternForm.php @@ -9,24 +9,11 @@ use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\pathauto\AliasTypeManager; use Symfony\Component\DependencyInjection\ContainerInterface; class ConfigurePatternForm extends FormBase { - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static(); - } - - /** - * {@inheritdoc} - */ - function __construct() { - - } - /** * {@inheritdoc} */ @@ -38,6 +25,12 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ + $pathauto_pattern = $cached_values['pathauto_pattern']; + $aliasType = $pathauto_pattern->getAliasType(); + $form = $aliasType->buildConfigurationForm($form, $form_state); + $form['default']['#default_value'] = $pathauto_pattern->getPattern(); return $form; } @@ -45,7 +38,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ + $pathauto_pattern = $cached_values['pathauto_pattern']; + $pathauto_pattern->setPattern($form_state->getValue('default')); } } diff --git a/src/Form/CriteriaDelete.php b/src/Form/CriteriaDelete.php index 5909f41..260d94d 100644 --- a/src/Form/CriteriaDelete.php +++ b/src/Form/CriteriaDelete.php @@ -12,10 +12,17 @@ use Drupal\ctools\Form\ConditionDelete; class CriteriaDelete extends ConditionDelete { + + /** + * {@inheritdoc} + */ protected function getRouteInfo() { return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'selection_criteria']]; } + /** + * {@inheritdoc} + */ protected function getConditions($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; @@ -26,6 +33,9 @@ protected function getConditions($cached_values) { return $conditions; } + /** + * {@inheritdoc} + */ protected function setConditions($cached_values, $conditions) { $old_conditions = $this->getConditions($cached_values); $diff = array_diff(array_keys($old_conditions), array_keys($conditions)); @@ -38,16 +48,13 @@ protected function setConditions($cached_values, $conditions) { return $cached_values; } + /** + * {@inheritdoc} + */ protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; - // @todo This is a total hack. The plugin that getType() represents should - // be responsible for this. - $type = $pattern->getType(); - $context_definition = new ContextDefinition('entity:' . $type); - return [ - $type => new Context($context_definition), - ]; + return $pattern->getAliasType()->getContexts(); } } diff --git a/src/Form/CriteriaForm.php b/src/Form/CriteriaForm.php index d74736e..aad718e 100644 --- a/src/Form/CriteriaForm.php +++ b/src/Form/CriteriaForm.php @@ -59,13 +59,7 @@ protected function setConditions($cached_values, $conditions) { protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; - // @todo This is a total hack. The plugin that getType() represents should - // be responsible for this. - $type = $pattern->getType(); - $context_definition = new ContextDefinition('entity:' . $type); - return [ - $type => new Context($context_definition), - ]; + return $pattern->getAliasType()->getContexts(); } } \ No newline at end of file diff --git a/src/Form/SelectionCriteriaForm.php b/src/Form/SelectionCriteriaForm.php index 0e9f5e6..164433c 100644 --- a/src/Form/SelectionCriteriaForm.php +++ b/src/Form/SelectionCriteriaForm.php @@ -1,9 +1,7 @@ getType(); - $context_definition = new ContextDefinition('entity:' . $type); - return [ - $type => new Context($context_definition), - ]; + return $pattern->getAliasType()->getContexts(); } } diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 9a3b70b..6b34adc 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -330,15 +330,31 @@ public function getPunctuationCharacters() { /** * {@inheritdoc} */ - public function createAlias($module, $op, $source, $data, $type = NULL, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $config = $this->configFactory->get('pathauto.settings'); - + public function createAlias(EntityInterface $entity, $op) { // Retrieve and apply the pattern for this content type. - $pattern = $this->getPatternByEntity($module, $type, $langcode); + $pattern = $this->getPatternByEntity($entity); + if (empty($pattern)) { + // No pattern? Do nothing (otherwise we may blow away existing aliases...) + return NULL; + } + + $entity_type_id = $entity->getEntityTypeId(); + $source = $entity->urlInfo()->toString(); + $config = $this->configFactory->get('pathauto.settings'); + $langcode = $entity->language()->getId(); + if ($entity->getEntityType()->getBundleEntityType()) { + $type = $entity->bundle(); + } + else { + $type = NULL; + } + $data = [ + $entity_type_id => $entity + ]; // Allow other modules to alter the pattern. $context = array( - 'module' => $module, + 'module' => $entity_type_id, 'op' => $op, 'source' => $source, 'data' => $data, @@ -347,11 +363,6 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod ); $this->moduleHandler->alter('pathauto_pattern', $pattern, $context); - if (empty($pattern)) { - // No pattern? Do nothing (otherwise we may blow away existing aliases...) - return NULL; - } - // Special handling when updating an item which is already aliased. $existing_alias = NULL; if ($op == 'update' || $op == 'bulkupdate') { @@ -370,7 +381,7 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod // Uses callback option to clean replacements. No sanitization. // Pass empty BubbleableMetadata object to explicitly ignore cacheablity, // as the result is never rendered. - $alias = $this->token->replace($pattern, $data, array( + $alias = $this->token->replace($pattern->getPattern(), $data, array( 'clear' => TRUE, 'callback' => array($this, 'cleanTokenValues'), 'langcode' => $langcode, @@ -380,7 +391,7 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod // Check if the token replacement has not actually replaced any values. If // that is the case, then stop because we should not generate an alias. // @see token_scan() - $pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern); + $pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern->getPattern()); if ($alias === $pattern_tokens_removed) { return NULL; } @@ -426,31 +437,31 @@ public function createAlias($module, $op, $source, $data, $type = NULL, $langcod /** * {@inheritdoc} */ - public function getPatternByEntity($entity_type_id, $bundle = '', $language = LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $config = $this->configFactory->get('pathauto.pattern'); - - $pattern_id = "$entity_type_id:$bundle:$language"; - if (!isset($this->patterns[$pattern_id])) { - $pattern = ''; - $variables = array(); - if ($language != LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $variables[] = "{$entity_type_id}.bundles.{$bundle}.languages.{$language}"; - } - if ($bundle) { - $variables[] = "{$entity_type_id}.bundles.{$bundle}.default"; - } - $variables[] = "{$entity_type_id}.default"; - - foreach ($variables as $variable) { - if ($pattern = trim($config->get('patterns.' . $variable))) { - break; + public function getPatternByEntity(EntityInterface $entity) { + if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()])) { + foreach (\Drupal::service('plugin.manager.alias_type')->getPluginDefinitionByType($entity->getEntityTypeId()) as $plugin_id => $plugin_definition) { + /** @var \Drupal\pathauto\PathautoPatternInterface[] $patterns */ + $patterns = \Drupal::entityManager() + ->getStorage('pathauto_pattern') + ->loadByProperties(['type' => $plugin_id]); + uasort($patterns, function (PathautoPatternInterface $a, PathautoPatternInterface $b) { + if ($a->getWeight() == $b->getWeight()) { + return 0; + } + return ($a->getWeight() < $b->getWeight()) ? -1 : 1; + }); + foreach ($patterns as $pattern) { + if ($pattern->applies($entity)) { + $this->patterns[$entity->getEntityTypeId()][$entity->id()] = $pattern; + } } } - - $this->patterns[$pattern_id] = $pattern; + // If still not set. + if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()])) { + $this->patterns[$entity->getEntityTypeId()][$entity->id()] = NULL; + } } - - return $this->patterns[$pattern_id]; + return $this->patterns[$entity->getEntityTypeId()][$entity->id()]; } /** @@ -477,10 +488,9 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array $options += array('language' => $entity->language()->getId()); $type = $entity->getEntityTypeId(); - $bundle = $entity->bundle(); // Skip processing if the entity has no pattern. - if (!$this->getPatternByEntity($type, $bundle, $options['language'])) { + if (!$this->getPatternByEntity($entity)) { return NULL; } @@ -493,8 +503,7 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } } - $result = $this->createAlias( - $type, $op, '/' . $entity->urlInfo()->getInternalPath(), array($type => $entity), $bundle, $options['language']); + $result = $this->createAlias($entity, $op); if ($type == 'taxonomy_term' && empty($options['is_child'])) { // For all children generate new aliases. diff --git a/src/PathautoManagerInterface.php b/src/PathautoManagerInterface.php index 9d56abd..90c8afa 100644 --- a/src/PathautoManagerInterface.php +++ b/src/PathautoManagerInterface.php @@ -82,44 +82,27 @@ public function cleanString($string, array $options = array()); /** * Load an URL alias pattern by entity, bundle, and language. * - * @param $entity_type_id - * An entity (e.g. node, taxonomy, user, etc.) - * @param $bundle - * A bundle (e.g. content type, vocabulary ID, etc.) - * @param $language - * A language code, defaults to the LANGUAGE_NONE constant. + * @param \Drupal\Core\Entity\EntityInterface $entity + * An entity. + * @return \Drupal\pathauto\PathautoPatternInterface|NULL */ - public function getPatternByEntity($entity_type_id, $bundle = '', $language = LanguageInterface::LANGCODE_NOT_SPECIFIED); + public function getPatternByEntity(EntityInterface $entity); /** * Apply patterns to create an alias. * - * @param string $module - * The name of your module (e.g., 'node'). + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. * @param string $op * Operation being performed on the content being aliased * ('insert', 'update', 'return', or 'bulkupdate'). - * @param string $source - * An internal Drupal path to be aliased. - * @param array $data - * An array of keyed objects to pass to token_replace(). For simple - * replacement scenarios 'node', 'user', and others are common keys, with an - * accompanying node or user object being the value. Some token types, like - * 'site', do not require any explicit information from $data and can be - * replaced even if it is empty. - * @param string $type - * For modules which provided pattern items in hook_pathauto(), - * the relevant identifier for the specific item to be aliased - * (e.g., $node->type). - * @param string $langcode - * A string specify the path's language. * * @return array|string * The alias that was created. * * @see _pathauto_set_alias() */ - public function createAlias($module, $op, $source, $data, $type = NULL, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED); + public function createAlias(EntityInterface $entity, $op); /** * Return an array of arrays for punctuation values. diff --git a/src/PathautoPatternInterface.php b/src/PathautoPatternInterface.php index bd5bcb0..5ca22b9 100644 --- a/src/PathautoPatternInterface.php +++ b/src/PathautoPatternInterface.php @@ -35,6 +35,11 @@ public function setPattern($pattern); */ public function getType(); + /** + * @return \Drupal\pathauto\AliasTypeInterface + */ + public function getAliasType(); + /** * Gets the weight of this pattern (compared to other patterns of this type). * @@ -98,4 +103,14 @@ public function removeSelectionCondition($condition_id); */ public function getSelectionLogic(); + /** + * Determines if this pattern can apply a given object. + * + * @param $object + * The object used to determine if this plugin can apply. + * + * @return bool + */ + public function applies($object); + } diff --git a/src/PathautoPatternListBuilder.php b/src/PathautoPatternListBuilder.php index 9c2c8d6..60aff73 100644 --- a/src/PathautoPatternListBuilder.php +++ b/src/PathautoPatternListBuilder.php @@ -9,6 +9,7 @@ use Drupal\Core\Config\Entity\ConfigEntityListBuilder; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Url; /** * Provides a listing of Pathauto pattern entities. @@ -33,4 +34,14 @@ public function buildRow(EntityInterface $entity) { return $row + parent::buildRow($entity); } + /** + * {@inheritdoc} + */ + public function getDefaultOperations(EntityInterface $entity) { + $operations = parent::getDefaultOperations($entity); + $operations['edit']['url'] = new Url('entity.pathauto_pattern.edit_form', ['machine_name' => $entity->id(), 'step' => 'general']); + + return $operations; + } + } diff --git a/src/Plugin/Deriver/EntityAliasTypeDeriver.php b/src/Plugin/Deriver/EntityAliasTypeDeriver.php new file mode 100644 index 0000000..7d80ab9 --- /dev/null +++ b/src/Plugin/Deriver/EntityAliasTypeDeriver.php @@ -0,0 +1,27 @@ +entityManager->getDefinitions() as $entity_type_id => $entity_type) { + if ($entity_type->hasLinkTemplate('canonical')) { + $this->derivatives[$entity_type_id] = $base_plugin_definition; + $this->derivatives[$entity_type_id]['label'] = $this->t('@label', ['@label' => $entity_type->getLabel()]); + $this->derivatives[$entity_type_id]['types'] = [$entity_type_id]; + $this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider(); + $this->derivatives[$entity_type_id]['context'] = [ + "$entity_type_id" => new ContextDefinition("entity:$entity_type_id", $this->t('@label', ['@label' => $entity_type->getLabel()])) + ]; + } + } + return $this->derivatives; + } +} \ No newline at end of file diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 7cdd951..893a998 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -12,14 +12,20 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\Core\Plugin\PluginBase; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Plugin\ContextAwarePluginBase; use Drupal\pathauto\AliasTypeInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * A base class for Alias Type plugins. + * A pathauto alias type plugin for entities with canonical links. + * + * @AliasType( + * id = "canonical_entities", + * deriver = "\Drupal\pathauto\Plugin\Deriver\EntityAliasTypeDeriver" + * ) */ -abstract class EntityAliasTypeBase extends PluginBase implements AliasTypeInterface { +class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -42,6 +48,13 @@ abstract class EntityAliasTypeBase extends PluginBase implements AliasTypeInterf */ protected $entityManager; + /** + * The path prefix for this entity type. + * + * @var string + */ + protected $prefix; + /** * Constructs a NodeAliasType instance. * @@ -129,17 +142,12 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form = array( '#type' => 'details', '#title' => $this->getLabel(), - '#open' => TRUE, - '#tree' => TRUE, ); - // Prompt for the default pattern for this module. - $key = 'default'; - - $form[$key] = array( + $form['default'] = array( '#type' => 'textfield', '#title' => $this->getPatternDescription(), - '#default_value' => $this->configuration['default'], + '#default_value' => !empty($this->configuration['default']) ? $this->configuration['default'] : '', '#size' => 65, '#maxlength' => 1280, '#element_validate' => array('token_element_validate'), @@ -148,24 +156,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#min_tokens' => 1, ); - // If the module supports a set of specialized patterns, set - // them up here. - $patterns = $this->getPatterns(); - foreach ($patterns as $itemname => $itemlabel) { - $key = 'default'; - $form['bundles'][$itemname][$key] = array( - '#type' => 'textfield', - '#title' => $itemlabel, - '#default_value' => isset($this->configuration['bundles'][$itemname][$key]) ? $this->configuration['bundles'][$itemname][$key] : NULL, - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $this->getTokenTypes(), - '#min_tokens' => 1, - ); - } - // Show the token help relevant to this pattern type. $form['token_help'] = array( '#theme' => 'token_tree', @@ -175,31 +165,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } - /** - * {@inheritdoc} - */ - public function getPatterns() { - $patterns = []; - $languages = $this->languageManager->getLanguages(); - if ($this->entityManager->getDefinition($this->getPluginId())->hasKey('bundle')) { - foreach ($this->getBundles() as $bundle => $bundle_label) { - if (count($languages) && $this->isContentTranslationEnabled($bundle)) { - $patterns[$bundle] = $this->t('Default path pattern for @bundle (applies to all @bundle fields with blank patterns below)', array('@bundle' => $bundle_label)); - foreach ($languages as $language) { - $patterns[$bundle . '_' . $language->getId()] = $this->t('Pattern for all @language @bundle paths', array( - '@bundle' => $bundle_label, - '@language' => $language->getName() - )); - } - } - else { - $patterns[$bundle] = $this->t('Pattern for all @bundle paths', array('@bundle' => $bundle_label)); - } - } - } - return $patterns; - } - /** * {@inheritdoc} */ @@ -249,9 +214,33 @@ protected function isContentTranslationEnabled($bundle) { public function applies($object) { $definition = $this->entityManager->getDefinition($this->getDerivativeId()); $class = $definition->getClass(); - if ($object instanceof $class) { - return TRUE; + return ($object instanceof $class); + } + + /** + * {@inheritdoc} + */ + public function getPatternDescription() { + return $this->t('Replace this description with proper annotation effort.'); + } + + /** + * {@inheritdoc} + */ + public function getSourcePrefix() { + if (empty($this->prefix)) { + $entity_type = $this->entityManager->getDefinition($this->getDerivativeId()); + $path = $entity_type->getLinkTemplate('canonical'); + // Remove slug(s)... This could probably be done cleaner, but I'm not in the mood. + $path_parts = explode('/', $path); + foreach ($path_parts as $key => $value) { + if (strpos($value, '}') === 0 && strpos($value, '{') == -1) { + unset ($path_parts[$key]); + } + } + $this->prefix = implode('/', $path_parts); } - return FALSE; + return $this->prefix; } + } diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 155bf2b..95a6bf5 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -95,4 +95,16 @@ public function getSourcePrefix() { return 'forum/'; } + /** + * {@inheritdoc} + */ + public function applies($object) { + if (parent::applies($object)) { + /** @var \Drupal\taxonomy\TermInterface $object */ + $config_forum = $this->configFactory->get('forum.settings'); + return $object->getVocabularyId() == $config_forum->get('vocabulary'); + } + return FALSE; + } + } diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 4a0c24d..f350e06 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -89,4 +89,16 @@ public function getSourcePrefix() { return 'taxonomy/term/'; } + /** + * {@inheritdoc} + */ + public function applies($object) { + if (parent::applies($object)) { + /** @var \Drupal\taxonomy\TermInterface $object */ + $config_forum = $this->configFactory->get('forum.settings'); + return $object->getVocabularyId() != $config_forum->get('vocabulary'); + } + return FALSE; + } + } diff --git a/src/Wizard/PatternWizard.php b/src/Wizard/PatternWizard.php index e4c1fe9..4a2e6ce 100644 --- a/src/Wizard/PatternWizard.php +++ b/src/Wizard/PatternWizard.php @@ -8,6 +8,9 @@ use Drupal\ctools\Wizard\EntityFormWizardBase; +use Drupal\pathauto\Form\ConfigurePatternForm; +use Drupal\pathauto\Form\PathautoPatternForm; +use Drupal\pathauto\Form\SelectionCriteriaForm; /** * Custom form wizard for pathauto pattern configuration. @@ -49,16 +52,16 @@ public function getOperations() { return [ 'general' => [ 'title' => $this->t('General information'), - 'form' => '\Drupal\pathauto\Form\PathautoPatternForm' + 'form' => PathautoPatternForm::class, ], 'selection_criteria' => [ 'title' => $this->t('Selection criteria'), - 'form' => '\Drupal\pathauto\Form\SelectionCriteriaForm' + 'form' => SelectionCriteriaForm::class, ], 'pattern' => [ 'title' => $this->t('Configure pattern'), - 'form' => '\Drupal\pathauto\Form\ConfigurePatternForm' - ] + 'form' => ConfigurePatternForm::class, + ], ]; } From 23a897670d6bac15a3c423489524060653d26625 Mon Sep 17 00:00:00 2001 From: Kris Vanderwater Date: Mon, 26 Oct 2015 22:01:56 -0500 Subject: [PATCH 083/169] using mutable version of config object in submit handler, updating the PathautoWidgets to work with the method changes and special casing taxonomy_term entity token logic since it does not properly conform to the rest of core. --- src/Form/PathautoSettingsForm.php | 2 +- src/Plugin/Field/FieldWidget/PathautoWidget.php | 4 ++-- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index b8d8bcd..5fdb941 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -183,7 +183,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function submitForm(array &$form, FormStateInterface $form_state) { - $config = $this->config('pathauto.settings'); + $config = $this->configFactory()->getEditable('pathauto.settings'); $form_state->cleanValues(); foreach ($form_state->getValues() as $key => $value) { diff --git a/src/Plugin/Field/FieldWidget/PathautoWidget.php b/src/Plugin/Field/FieldWidget/PathautoWidget.php index 321ae73..1b3623e 100644 --- a/src/Plugin/Field/FieldWidget/PathautoWidget.php +++ b/src/Plugin/Field/FieldWidget/PathautoWidget.php @@ -52,7 +52,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen - $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity->getEntityTypeId(), $entity->bundle(), $entity->language()->getId()); + $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); if (empty($pattern)) { return $element; } @@ -62,7 +62,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if (!$entity->isNew()) { module_load_include('inc', 'pathauto'); $path = \Drupal::service('path.alias_manager')->getAliasByPath('/' . $entity->urlInfo()->getInternalPath(), $entity->language()->getId()); - $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', '/' . $entity->urlInfo()->getInternalPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); + $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity, 'return'); $entity->path->pathauto = ($path != '/' . $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); } else { diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 893a998..8bf253e 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -132,6 +132,12 @@ public function getLabel() { */ public function getTokenTypes() { $definition = $this->getPluginDefinition(); + // For some reason, we didn't unify token keys with entity types... + foreach ($definition['types'] as $key => $type) { + if ($type == 'taxonomy_term') { + $definition['types'][$key] = 'term'; + } + } return $definition['types']; } From 6245f307ec7c0e684a5ab64c1078deef1ae5e3a0 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 2 Nov 2015 11:49:01 -0600 Subject: [PATCH 084/169] fixing the settings for to appropriately retrieve punctuation handling values --- src/Form/PathautoSettingsForm.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 5fdb941..21729a2 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -159,7 +159,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { $punctuation = \Drupal::service('pathauto.manager')->getPunctuationCharacters(); foreach ($punctuation as $name => $details) { - $details['default'] = PathautoManagerInterface::PUNCTUATION_REMOVE; + if (!$config->get('punctuation.punctuation'. $name)) { + $details['default'] = PathautoManagerInterface::PUNCTUATION_REMOVE; + } + else { + $details['default'] = $config->get('punctuation.punctuation'. $name); + } if ($details['value'] == $config->get('separator')) { $details['default'] = PathautoManagerInterface::PUNCTUATION_REPLACE; } From 30acf0ed07adac677e476f73c03056a33ba807c5 Mon Sep 17 00:00:00 2001 From: Randomletters Date: Tue, 3 Nov 2015 13:06:45 -0500 Subject: [PATCH 085/169] Remove references to "node" in comments and variables. Replace with "entity". --- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 733b754..8234ef8 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -43,7 +43,7 @@ abstract class EntityAliasTypeBase extends PluginBase implements AliasTypeInterf protected $entityManager; /** - * Constructs a NodeAliasType instance. + * Constructs a EntityAliasTypeBase instance. * * @param array $configuration * A configuration array containing information about the plugin instance. @@ -225,7 +225,7 @@ public function batchUpdate(&$context) { if (!isset($context['sandbox']['total'])) { $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField(); - // If there are no nodes to update, the stop immediately. + // If there are no entities to update, then stop immediately. if (!$context['sandbox']['total']) { $context['finished'] = 1; return; @@ -267,12 +267,12 @@ protected function bulkUpdate(array $ids, array $options = array()) { $options += array('message' => FALSE); $entities = $this->entityManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); - foreach ($entities as $node) { - \Drupal::service('pathauto.manager')->updateAlias($node, 'bulkupdate', $options); + foreach ($entities as $entity) { + \Drupal::service('pathauto.manager')->updateAlias($entity, 'bulkupdate', $options); } if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated URL alias for 1 node.', 'Updated URL aliases for @count nodes.')); + drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated URL alias for 1 entity.', 'Updated URL aliases for @count entities.')); } } From 8984a6c9e342be995669e2e0a1aec081038be78e Mon Sep 17 00:00:00 2001 From: giancarlosotelo Date: Thu, 5 Nov 2015 16:13:01 +0100 Subject: [PATCH 086/169] Replace checkPlain() function and added html entities decode --- src/AliasCleaner.php | 4 +++- src/Tests/PathautoUnitTest.php | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index 889fc15..f6dc672 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -9,6 +9,7 @@ use Drupal\Component\Render\PlainTextOutput; use Drupal\Component\Transliteration\TransliterationInterface; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; @@ -229,7 +230,8 @@ public function cleanString($string, array $options = array()) { } // Remove all HTML tags from the string. - $output = PlainTextOutput::renderFromHtml($string); + $output = Html::decodeEntities($string); + $output = PlainTextOutput::renderFromHtml($output); // Optionally transliterate. if ($this->cleanStringCache['transliterate']) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 5d4497f..4925ae7 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -7,7 +7,7 @@ namespace Drupal\pathauto\Tests; -use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Utility\Html; use Drupal\Core\Language\Language; use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; @@ -139,7 +139,7 @@ public function testCleanString() { // Test that HTML tags are removed. $tests['This text has
HTML tags.'] = 'text-has-html-tags'; - $tests[(string) SafeMarkup::checkPlain('This text has
HTML tags.')] = 'text-has-html-tags'; + $tests[Html::escape('This text has
HTML tags.')] = 'text-has-html-tags'; // Transliteration. $tests['ľščťžýáíéňô'] = 'lsctzyaieno'; From 67c880b1923b1c224b3f885f3bd3824a654c6ab3 Mon Sep 17 00:00:00 2001 From: Randomletters Date: Fri, 6 Nov 2015 16:23:15 -0500 Subject: [PATCH 087/169] Change wording of message based on feedback --- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 8234ef8..b716e9d 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -272,7 +272,7 @@ protected function bulkUpdate(array $ids, array $options = array()) { } if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated URL alias for 1 entity.', 'Updated URL aliases for @count entities.')); + drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated 1 URL alias for type %label.', 'Updated @count URL aliases for type %label.'), array('%label' => $entity->getLabel())); } } From 593ed20f100487b049a821dae71584eafbaf675f Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 09:41:02 -0600 Subject: [PATCH 088/169] added context definitions to the pathauto entity schema --- config/schema/pathauto_pattern.schema.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/schema/pathauto_pattern.schema.yml b/config/schema/pathauto_pattern.schema.yml index c42065f..6d805ba 100644 --- a/config/schema/pathauto_pattern.schema.yml +++ b/config/schema/pathauto_pattern.schema.yml @@ -28,3 +28,9 @@ pathauto.pathauto_pattern.*: weight: type: integer label: 'Weight' + context_definitions: + type: sequence + label: 'Context definitions' + sequence: + - type: ctools.relationship + label: 'Context' From 734e9a38585e46c1805a5d9cf88663d20d01cdf4 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 09:41:43 -0600 Subject: [PATCH 089/169] adding relationship routes for adding, editing and deleting relationships in the UI. --- pathauto.routing.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pathauto.routing.yml b/pathauto.routing.yml index dbc4915..c2c123e 100644 --- a/pathauto.routing.yml +++ b/pathauto.routing.yml @@ -32,6 +32,33 @@ entity.pathauto_pattern.delete_form: requirements: _permission: 'administer pathauto' +pathauto.pattern.relationship.add: + path: '/admin/config/search/path/patterns/{machine_name}/relationship/{context}/add' + defaults: + _form: '\Drupal\pathauto\Form\ContextConfigure' + _title: 'Configure relationship' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +pathauto.pattern.relationship.edit: + path: '/admin/config/search/path/patterns/{machine_name}/relationship/{context}/edit' + defaults: + _form: '\Drupal\pathauto\Form\ContextConfigure' + _title: 'Configure relationship' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + +pathauto.pattern.relationship.delete: + path: '/admin/config/search/path/patterns/{machine_name}/relationship/{id}/delete' + defaults: + _form: '\Drupal\pathauto\Form\ContextDelete' + _title: 'Delete relationship' + tempstore_id: 'pathauto.pattern' + requirements: + _permission: 'administer pathauto' + pathauto.pattern.condition.add: path: '/admin/config/search/path/patterns/{machine_name}/criteria/{condition}/add' defaults: From 3e322362de024a78ec81a63f3132b4ee529d1a59 Mon Sep 17 00:00:00 2001 From: Randomletters Date: Mon, 9 Nov 2015 11:19:45 -0500 Subject: [PATCH 090/169] Change label to EntityAliasTypeBase label. --- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index b716e9d..a5e0188 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -272,7 +272,7 @@ protected function bulkUpdate(array $ids, array $options = array()) { } if (!empty($options['message'])) { - drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated 1 URL alias for type %label.', 'Updated @count URL aliases for type %label.'), array('%label' => $entity->getLabel())); + drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated 1 %label URL alias.', 'Updated @count %label URL aliases.'), array('%label' => $this->getLabel())); } } From 641765e0a1400d7ce88af1531d5b842ddc6dfd3b Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:48:16 -0600 Subject: [PATCH 091/169] added helper methods for handling contexts to the pathauto pattern entity interface and implemented them. Also provided preSave and postLoad overrides for the pathauto pattern entity in order to properly handle expanding and contracting relationship settings. --- src/Entity/PathautoPattern.php | 126 ++++++++++++++++++++++++++++++- src/PathautoPatternInterface.php | 41 ++++++++++ 2 files changed, 165 insertions(+), 2 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index bd10973..77fa074 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -7,10 +7,13 @@ namespace Drupal\pathauto\Entity; -use Drupal\Component\Plugin\ContextAwarePluginInterface; use Drupal\Core\Condition\ConditionPluginCollection; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\Core\Plugin\Context\ContextInterface; +use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; use Drupal\pathauto\PathautoPatternInterface; @@ -101,6 +104,16 @@ class PathautoPattern extends ConfigEntityBase implements PathautoPatternInterfa */ protected $weight = 0; + /** + * @var \Drupal\Core\Plugin\Context\ContextInterface[] + */ + protected $contexts = []; + + /** + * @var array + */ + protected $context_definitions = []; + /** * The plugin collection that holds the selection criteria condition plugins. * @@ -121,6 +134,41 @@ public function preSave(EntityStorageInterface $storage) { $criteria[$id] = $condition->getConfiguration(); } $this->selection_criteria = $criteria; + + /** @var \Drupal\Core\Plugin\Context\ContextInterface[] $contexts */ + $contexts = $this->getContexts(); + foreach ($this->getAliasType()->getContexts() as $plugin_context_id => $plugin_context) { + unset($contexts[$plugin_context_id]); + } + $this->context_definitions = []; + foreach ($contexts as $context_id => $context) { + $this->context_definitions[] = [ + 'id' => $context_id, + 'label' => $context->getContextDefinition()->getLabel() + ]; + } + } + + public static function postLoad(EntityStorageInterface $storage, array &$entities) { + /** @var \Drupal\ctools\TypedDataResolver $resolver */ + $resolver = \Drupal::service('ctools.typed_data.resolver'); + /** @var \Drupal\pathauto\Entity\PathautoPattern $entity */ + foreach ($entities as $entity) { + foreach ($entity->getContextDefinitions() as $definition) { + $context = $resolver->convertTokenToContext($definition['id'], $entity->getContexts()); + $new_definition = new ContextDefinition( + $context->getContextDefinition()->getDataType(), + $definition['label'], + $context->getContextDefinition()->isRequired(), + $context->getContextDefinition()->isMultiple(), + $context->getContextDefinition()->getDescription(), + $context->getContextDefinition()->getDefaultValue() + ); + $new_context = new Context($new_definition, $context->hasContextValue() ? $context->getContextValue() : NULL); + $entity->addContext($definition['id'], $new_context); + } + } + parent::postLoad($storage, $entities); } /** @@ -185,6 +233,70 @@ public function setWeight($weight) { return $this; } + /** + * {@inheritdoc} + */ + public function hasContext($token) { + $contexts = $this->getAliasType()->getContexts(); + $contexts += $this->contexts; + return !empty($contexts[$token]); + } + + /** + * {@inheritdoc} + */ + public function getContext($token) { + $contexts = $this->getAliasType()->getContexts(); + $contexts += $this->contexts; + return $contexts[$token]; + } + + /** + * {@inheritdoc} + */ + public function getContexts() { + $contexts = $this->getAliasType()->getContexts(); + $contexts += $this->contexts; + return $contexts; + } + + /** + * {@inheritdoc} + */ + public function addContext($token, ContextInterface $context) { + if (!$this->hasContext($token)) { + $this->contexts[$token] = $context; + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function replaceContext($token, ContextInterface $context) { + if ($this->hasContext($token)) { + $this->contexts[$token] = $context; + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function removeContext($token) { + if (isset($this->contexts[$token])) { + unset($this->contexts[$token]); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function getContextDefinitions() { + return $this->context_definitions; + } + /** * {@inheritdoc} */ @@ -238,7 +350,17 @@ public function applies($object) { $keys = array_keys($definitions); // Set the context object on our Alias plugin before retrieving contexts. $this->getAliasType()->setContextValue($keys[0], $object); - $contexts = $this->getAliasType()->getContexts(); + /** @var \Drupal\Core\Plugin\Context\ContextInterface[] $base_contexts */ + $base_contexts = $this->getAliasType()->getContexts(); + $contexts = []; + foreach ($base_contexts as $context_id => $base_context) { + $contexts[$context_id] = new Context($base_context->getContextDefinition(), $object); + } + /** @var \Drupal\ctools\TypedDataResolver $resolver */ + $resolver = \Drupal::service('ctools.typed_data.resolver'); + foreach ($this->getContexts() as $token => $context) { + $contexts[$token] = $resolver->convertTokenToContext($token, $contexts); + } /** @var \Drupal\Core\Plugin\Context\ContextHandler $context_handler */ $context_handler = \Drupal::service('context.handler'); $conditions = $this->getSelectionConditions(); diff --git a/src/PathautoPatternInterface.php b/src/PathautoPatternInterface.php index 5ca22b9..c716ece 100644 --- a/src/PathautoPatternInterface.php +++ b/src/PathautoPatternInterface.php @@ -8,6 +8,7 @@ namespace Drupal\pathauto; use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Plugin\Context\ContextInterface; /** * Provides an interface for defining Pathauto pattern entities. @@ -57,6 +58,46 @@ public function getWeight(); */ public function setWeight($weight); + /** + * @return bool + */ + public function hasContext($token); + + /** + * @return \Drupal\Core\Plugin\Context\ContextInterface + */ + public function getContext($token); + + /** + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + */ + public function getContexts(); + + /** + * @param string $token + * @param \Drupal\Core\Plugin\Context\ContextInterface $context + * + * @return $this + */ + public function addContext($token, ContextInterface $context); + + /** + * @param string $token + * @param \Drupal\Core\Plugin\Context\ContextInterface $context + * + * @return $this + */ + public function replaceContext($token, ContextInterface $context); + + /** + * @param string $token + * + * @return $this + */ + public function removeContext($token); + + public function getContextDefinitions(); + /** * Gets the selection condition collection. * From 993141a42e30e639cb376421c8ac5ab1819ad617 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:50:19 -0600 Subject: [PATCH 092/169] Adding additional handling to the pattern configuration wizard step in order to populate with appropriate tokens from available relationships. --- src/Form/ConfigurePatternForm.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Form/ConfigurePatternForm.php b/src/Form/ConfigurePatternForm.php index 24225db..be4e47d 100644 --- a/src/Form/ConfigurePatternForm.php +++ b/src/Form/ConfigurePatternForm.php @@ -31,6 +31,23 @@ public function buildForm(array $form, FormStateInterface $form_state) { $aliasType = $pathauto_pattern->getAliasType(); $form = $aliasType->buildConfigurationForm($form, $form_state); $form['default']['#default_value'] = $pathauto_pattern->getPattern(); + // This is rough but generally seems right. I think Token is not nuanced + // enough to handle this. + $tokens = $form['default']['#token_types']; + $contexts = $pathauto_pattern->getContexts(); + foreach ($contexts as $context_id => $context) { + list($data_type, $entity_type) = explode(':', $context->getContextDefinition()->getDataType()); + if ($data_type == 'entity') { + if ($entity_type == 'taxonomy_term') { + $entity_type = 'term'; + } + if (!in_array($entity_type, $tokens)) { + $tokens[] = $entity_type; + } + } + } + $form['default']['#token_types'] = $tokens; + return $form; } From e595d07b7008dbb31c6ac2d3761190997b6291df Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:50:51 -0600 Subject: [PATCH 093/169] moving to the entity getContexts() method instead of directly delegating to the plugin --- src/Form/CriteriaForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/CriteriaForm.php b/src/Form/CriteriaForm.php index aad718e..f7f62cd 100644 --- a/src/Form/CriteriaForm.php +++ b/src/Form/CriteriaForm.php @@ -59,7 +59,7 @@ protected function setConditions($cached_values, $conditions) { protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; - return $pattern->getAliasType()->getContexts(); + return $pattern->getContexts(); } } \ No newline at end of file From 3322c08b857c643f83a4f44eaf22aebb43fdebb9 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:57:11 -0600 Subject: [PATCH 094/169] moving to the entity getContexts() method instead of directly delegating to the plugin --- src/Form/SelectionCriteriaForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/SelectionCriteriaForm.php b/src/Form/SelectionCriteriaForm.php index 164433c..4dad025 100644 --- a/src/Form/SelectionCriteriaForm.php +++ b/src/Form/SelectionCriteriaForm.php @@ -67,7 +67,7 @@ protected function getConditions($cached_values) { protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; - return $pattern->getAliasType()->getContexts(); + return $pattern->getContexts(); } } From f630a94b6393a8bdc691cd66a0f849511f324afd Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:57:50 -0600 Subject: [PATCH 095/169] adding a more appropriate label to the derived plugin context definitions --- src/Plugin/Deriver/EntityAliasTypeDeriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/Deriver/EntityAliasTypeDeriver.php b/src/Plugin/Deriver/EntityAliasTypeDeriver.php index 7d80ab9..3d272cf 100644 --- a/src/Plugin/Deriver/EntityAliasTypeDeriver.php +++ b/src/Plugin/Deriver/EntityAliasTypeDeriver.php @@ -18,7 +18,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { $this->derivatives[$entity_type_id]['types'] = [$entity_type_id]; $this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider(); $this->derivatives[$entity_type_id]['context'] = [ - "$entity_type_id" => new ContextDefinition("entity:$entity_type_id", $this->t('@label', ['@label' => $entity_type->getLabel()])) + "$entity_type_id" => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()])) ]; } } From e0681c3b58be743b02543918e579c682d795b605 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:58:14 -0600 Subject: [PATCH 096/169] adding support for relationship contexts. --- src/Wizard/PatternWizard.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Wizard/PatternWizard.php b/src/Wizard/PatternWizard.php index 4a2e6ce..21f08af 100644 --- a/src/Wizard/PatternWizard.php +++ b/src/Wizard/PatternWizard.php @@ -8,6 +8,7 @@ use Drupal\ctools\Wizard\EntityFormWizardBase; +use Drupal\pathauto\Form\AddContext; use Drupal\pathauto\Form\ConfigurePatternForm; use Drupal\pathauto\Form\PathautoPatternForm; use Drupal\pathauto\Form\SelectionCriteriaForm; @@ -54,6 +55,10 @@ public function getOperations() { 'title' => $this->t('General information'), 'form' => PathautoPatternForm::class, ], + 'contexts' => [ + 'title' => $this->t('Add contexts'), + 'form' => AddContext::class, + ], 'selection_criteria' => [ 'title' => $this->t('Selection criteria'), 'form' => SelectionCriteriaForm::class, From 124c93a98a117bfa27001df44f11905b9784fcc7 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 10:59:42 -0600 Subject: [PATCH 097/169] adding the necessary form for adding contexts. These should ultimate be subclasses of CTools provided classes. --- src/Form/AddContext.php | 204 ++++++++++++++++++++++++++++++++++ src/Form/ContextConfigure.php | 184 ++++++++++++++++++++++++++++++ src/Form/ContextDelete.php | 129 +++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 src/Form/AddContext.php create mode 100644 src/Form/ContextConfigure.php create mode 100644 src/Form/ContextDelete.php diff --git a/src/Form/AddContext.php b/src/Form/AddContext.php new file mode 100644 index 0000000..a1d6e49 --- /dev/null +++ b/src/Form/AddContext.php @@ -0,0 +1,204 @@ +getTemporaryValue('wizard'); + $this->machine_name = $cached_values['id']; + $form['items'] = array( + '#type' => 'markup', + '#prefix' => '

', + '#suffix' => '
', + '#theme' => 'table', + '#header' => array($this->t('Context ID'), $this->t('Label'), $this->t('Data Type'), $this->t('Options')), + '#rows' => $this->renderRows($cached_values), + '#empty' => t('No contexts or relationships have been added.') + ); + + $form['relationships'] = [ + '#type' => 'select', + '#title' => $this->t('Add a relationship'), + '#options' => $this->getAvailableRelationships($cached_values), + ]; + $form['add_relationship'] = [ + '#type' => 'submit', + '#name' => 'add', + '#value' => t('Add Relationship'), + '#ajax' => [ + 'callback' => [$this, 'addRelationship'], + 'event' => 'click', + ], + '#submit' => [ + 'callback' => [$this, 'submitForm'], + ] + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + if ($form_state->getTriggeringElement()['#name'] == 'add') { + list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $form_state->getValue('relationships')); + $form_state->setRedirect($this->getAddRoute(), $route_parameters); + } + } + + public function addRelationship(array &$form, FormStateInterface $form_state) { + $relationship = $form_state->getValue('relationships'); + $content = \Drupal::formBuilder()->getForm($this->getContextClass(), $relationship, $this->getTempstoreId(), $this->machine_name); + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $relationship); + $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getAddRoute(), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($this->t('Configure Relationship'), $content, array('width' => '700'))); + return $response; + } + + protected function getAvailableRelationships($cached_values) { + /** @var \Drupal\ctools\TypedDataResolver $resolver */ + $resolver = \Drupal::service('ctools.typed_data.resolver'); + return $resolver->getTokensOfDataType($this->getContexts($cached_values), $this->property_types); + } + + /** + * @param $cached_values + * + * @return array + */ + protected function renderRows($cached_values) { + $contexts = array(); + foreach ($this->getContexts($cached_values) as $row => $context) { + list($route_name, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $row); + $build = array( + '#type' => 'operations', + '#links' => $this->getOperations($row, $route_name, $route_parameters), + ); + $contexts[$row] = array( + $row, + $context->getContextDefinition()->getLabel(), + $context->getContextDefinition()->getDataType(), + 'operations' => [ + 'data' => $build, + ], + ); + } + return $contexts; + } + + protected function getOperations($row, $route_name_base, array $route_parameters = array()) { + // Base contexts will not be a : separated and generated relationships should have 3 parts. + if (count(explode(':', $row)) < 2) { + return []; + } + $operations['edit'] = array( + 'title' => t('Edit'), + 'url' => new Url($route_name_base . '.edit', $route_parameters), + 'weight' => 10, + 'attributes' => array( + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ), + ); + $route_parameters['id'] = $route_parameters['context']; + $operations['delete'] = array( + 'title' => t('Delete'), + 'url' => new Url($route_name_base . '.delete', $route_parameters), + 'weight' => 100, + 'attributes' => array( + 'class' => array('use-ajax'), + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ), + ); + return $operations; + } + + /** + * Return a subclass of '\Drupal\ctools\Form\ContextConfigure'. + * + * The ConditionConfigure class is designed to be subclassed with custom + * route information to control the modal/redirect needs of your use case. + * + * @return string + */ + protected function getContextClass() { + return ContextConfigure::class; + } + + /** + * The route to which condition 'add' actions should submit. + * + * @return string + */ + protected function getAddRoute() { + return 'pathauto.pattern.relationship.add'; + } + + /** + * Provide the tempstore id for your specified use case. + * + * @return string + */ + protected function getTempstoreId() { + return 'pathauto.pattern'; + } + + /** + * @param $cached_values + * + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + */ + protected function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + return $pattern->getContexts(); + } + + protected function getRelationshipOperationsRouteInfo($machine_name, $row) { + return ['pathauto.pattern.relationship', ['machine_name' => $machine_name, 'context' => $row]]; + } + +} diff --git a/src/Form/ContextConfigure.php b/src/Form/ContextConfigure.php new file mode 100644 index 0000000..2344ef9 --- /dev/null +++ b/src/Form/ContextConfigure.php @@ -0,0 +1,184 @@ +get('user.shared_tempstore'), $container->get('ctools.typed_data.resolver')); + } + + public function __construct(SharedTempStoreFactory $tempstore, TypedDataResolver $resolver) { + $this->tempstore = $tempstore; + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'ctools_context_configure'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $context = NULL, $tempstore_id = NULL, $machine_name = NULL) { + $this->tempstore_id = $tempstore_id; + $this->machine_name = $machine_name; + $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name); + + /** @var \Drupal\Core\Plugin\Context\ContextInterface[] $contexts */ + $contexts = $this->getContexts($cached_values); + $context_object = $this->resolver->convertTokenToContext($context, $contexts); + $form['id'] = [ + '#type' => 'value', + '#value' => $context + ]; + $form['context_object'] = [ + '#type' => 'value', + '#value' => $context_object, + ]; + $form['context_data'] = [ + '#type' => 'item', + '#title' => $this->resolver->getLabelByToken($context, $contexts), + '#markup' => $context_object->getContextDefinition()->getDataType(), + ]; + $form['label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Context label'), + '#default_value' => !empty($contexts[$context]) ? $contexts[$context]->getContextDefinition()->getLabel() : '', + '#required' => TRUE, + ]; + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Save'), + '#ajax' => [ + 'callback' => [$this, 'ajaxSave'], + ] + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name); + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + if (!$pattern->hasContext($form_state->getValue('id'))) { + /** @var \Drupal\Core\Plugin\Context\ContextInterface $context */ + $context = $form_state->getValue('context_object'); + $definition = $context->getContextDefinition(); + $new_definition = new ContextDefinition($definition->getDataType(), $form_state->getValue('label'), $definition->isRequired(), $definition->isMultiple(), $definition->getDescription(), $definition->getDefaultValue()); + $new_context = new Context($new_definition, $context->hasContextValue() ? $context->getContextValue() : NULL); + $pattern->addContext($form_state->getValue('id'), $new_context); + } + else { + $context = $pattern->getContext($form_state->getValue('id')); + $definition = $context->getContextDefinition(); + $new_definition = new ContextDefinition($definition->getDataType(), $form_state->getValue('label'), $definition->isRequired(), $definition->isMultiple(), $definition->getDescription(), $definition->getDefaultValue()); + $new_context = new Context($new_definition, $context->hasContextValue() ? $context->getContextValue() : NULL); + $pattern->replaceContext($form_state->getValue('id'), $new_context); + } + $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); + list($route_name, $route_options) = $this->getParentRouteInfo(); + $form_state->setRedirect($route_name, $route_options); + } + + public function ajaxSave(array &$form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + list($route_name, $route_parameters) = $this->getParentRouteInfo(); + $response->addCommand(new RedirectCommand($this->url($route_name, $route_parameters))); + $response->addCommand(new CloseModalDialogCommand()); + return $response; + } + + /** + * Document the route name and parameters for redirect after submission. + * + * @return array + * In the format of + * return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name']]; + */ + protected function getParentRouteInfo() { + return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'contexts']]; + } + + /** + * @param $context + * + * @return array + * In the format of + * return ['route.name', ['machine_name' => $this->machine_name, 'context' => $context]]; + */ + protected function getRouteInfo($context) { + return ['pathauto.pattern.relationship.add', ['machine_name' => $this->machine_name, 'context' => $context]]; + } + + /** + * Custom logic for setting the conditions array in cached_values. + * + * @param $cached_values + * + * @param $contexts + * The conditions to set within the cached values. + * + * @return mixed + * Return the $cached_values + */ + protected function setContexts($cached_values, $contexts) {} + + /** + * Custom logic for retrieving the contexts array from cached_values. + * + * @param $cached_values + * + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + */ + protected function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + return $pattern->getContexts(); + } + +} \ No newline at end of file diff --git a/src/Form/ContextDelete.php b/src/Form/ContextDelete.php new file mode 100644 index 0000000..8a4830a --- /dev/null +++ b/src/Form/ContextDelete.php @@ -0,0 +1,129 @@ +get('user.shared_tempstore'), $container->get('ctools.typed_data.resolver')); + } + + public function __construct(SharedTempStoreFactory $tempstore, TypedDataResolver $resolver) { + $this->tempstore = $tempstore; + $this->resolver = $resolver; + } + + public function getFormId() { + return 'ctools_context_delete'; + } + + public function getQuestion($id = NULL, $cached_values = NULL) { + $context = $this->getContexts($cached_values)[$id]; + return $this->t('Are you sure you want to delete the @label context?', [ + '@label' => $context->getContextDefinition()->getLabel(), + ]); + } + + public function getCancelUrl() { + return new Url('entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'contexts']); + } + + public function buildForm(array $form, FormStateInterface $form_state, $id = NULL, $tempstore_id = NULL, $machine_name = NULL) { + $this->tempstore_id = $tempstore_id; + $this->machine_name = $machine_name; + $this->id = $id; + + $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name); + $form ['#title'] = $this->getQuestion($id, $cached_values); + + $form ['#attributes']['class'][] = 'confirmation'; + $form ['description'] = array('#markup' => $this->getDescription()); + $form [$this->getFormName()] = array('#type' => 'hidden', '#value' => 1); + + // By default, render the form using theme_confirm_form(). + if (!isset($form ['#theme'])) { + $form ['#theme'] = 'confirm_form'; + } + $form['actions'] = array('#type' => 'actions'); + $form['actions'] += $this->actions($form, $form_state); + return $form; + } + + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);; + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + $pattern->removeContext($this->id); + $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); + $form_state->setRedirectUrl($this->getCancelUrl()); + } + + protected function actions(array $form, FormStateInterface $form_state) { + return array( + 'submit' => array( + '#type' => 'submit', + '#value' => $this->getConfirmText(), + '#validate' => array( + array($this, 'validate'), + ), + '#submit' => array( + array($this, 'submitForm'), + ), + ), + 'cancel' => ConfirmFormHelper::buildCancelLink($this, $this->getRequest()), + ); + } + + /** + * @param $cached_values + * + * @return \Drupal\Core\Plugin\Context\ContextInterface[] + */ + public function getContexts($cached_values) { + /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ + $pattern = $cached_values['pathauto_pattern']; + return $pattern->getContexts(); + } + +} From ea5680b3badbea6182b96cecfb4184a89ea7b4c7 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 12:23:27 -0600 Subject: [PATCH 098/169] removed the unnecessary menu entry --- pathauto.links.menu.yml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 pathauto.links.menu.yml diff --git a/pathauto.links.menu.yml b/pathauto.links.menu.yml deleted file mode 100644 index bec5554..0000000 --- a/pathauto.links.menu.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Pathauto pattern menu items definition -entity.pathauto_pattern.collection: - title: 'Pathauto pattern' - route_name: entity.pathauto_pattern.collection - description: 'List Pathauto pattern' - parent: system.admin_structure - From 41307c8ea654f75f4669425e967d6cca57fe2b83 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 12:33:17 -0600 Subject: [PATCH 099/169] minimizing the diff from merging --- src/PathautoManager.php | 118 ---------------------------------------- 1 file changed, 118 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 4a850f0..6f37c78 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -128,124 +128,6 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle $this->stringTranslation = $string_translation; } - /** - * {@inheritdoc} - */ - public function cleanString($string, array $options = array()) { - if (empty($this->cleanStringCache)) { - // Generate and cache variables used in this method. - $config = $this->configFactory->get('pathauto.settings'); - $this->cleanStringCache = array( - 'separator' => $config->get('separator'), - 'strings' => array(), - 'transliterate' => $config->get('transliterate'), - 'punctuation' => array(), - 'reduce_ascii' => (bool) $config->get('reduce_ascii'), - 'ignore_words_regex' => FALSE, - 'lowercase' => (bool) $config->get('case'), - 'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()), - ); - - // Generate and cache the punctuation replacements for strtr(). - $punctuation = $this->getPunctuationCharacters(); - foreach ($punctuation as $name => $details) { - $action = $config->get('punctuation.' . $name); - switch ($action) { - case PathautoManagerInterface::PUNCTUATION_REMOVE: - $cache['punctuation'][$details['value']] = ''; - $this->cleanStringCache; - - case PathautoManagerInterface::PUNCTUATION_REPLACE: - $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator']; - break; - - case PathautoManagerInterface::PUNCTUATION_DO_NOTHING: - // Literally do nothing. - break; - } - } - - // Generate and cache the ignored words regular expression. - $ignore_words = $config->get('ignore_words'); - $ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words); - if ($ignore_words_regex) { - $this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b'; - if (function_exists('mb_eregi_replace')) { - $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace'; - } - else { - $this->cleanStringCache['ignore_words_callback'] = 'preg_replace'; - $this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i'; - } - } - } - - // Empty strings do not need any processing. - if ($string === '' || $string === NULL) { - return ''; - } - - $langcode = NULL; - if (!empty($options['language'])) { - $langcode = $options['language']->getId(); - } - elseif (!empty($options['langcode'])) { - $langcode = $options['langcode']; - } - - // Check if the string has already been processed, and if so return the - // cached result. - if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) { - return $this->cleanStringCache['strings'][$langcode][(string) $string]; - } - - // Remove all HTML tags from the string. - $output = PlainTextOutput::renderFromHtml($string); - - // Optionally transliterate. - if ($this->cleanStringCache['transliterate']) { - // If the reduce strings to letters and numbers is enabled, don't bother - // replacing unknown characters with a question mark. Use an empty string - // instead. - $output = \Drupal::service('transliteration')->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?'); - } - - // Replace or drop punctuation based on user settings. - $output = strtr($output, $this->cleanStringCache['punctuation']); - - // Reduce strings to letters and numbers. - if ($this->cleanStringCache['reduce_ascii']) { - $output = preg_replace('/[^a-zA-Z0-9\/]+/', $this->cleanStringCache['separator'], $output); - } - - // Get rid of words that are on the ignore list. - if ($this->cleanStringCache['ignore_words_regex']) { - $words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output); - if (Unicode::strlen(trim($words_removed)) > 0) { - $output = $words_removed; - } - } - - // Always replace whitespace with the separator. - $output = preg_replace('/\s+/', $this->cleanStringCache['separator'], $output); - - // Trim duplicates and remove trailing and leading separators. - $output = $this->aliasCleaner->getCleanSeparators($this->aliasCleaner->getCleanSeparators($output, $this->cleanStringCache['separator'])); - - // Optionally convert to lower case. - if ($this->cleanStringCache['lowercase']) { - $output = Unicode::strtolower($output); - } - - // Shorten to a logical place based on word boundaries. - $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE); - - // Cache this result in the static array. - $this->cleanStringCache['strings'][$langcode][(string) $string] = $output; - - return $output; - } - /** * {@inheritdoc} */ From 1e6ad4e52d9e5d135605c2ac75f7817816f3a898 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 9 Nov 2015 12:39:18 -0600 Subject: [PATCH 100/169] removing unnecessary use statements --- src/PathautoManager.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 6f37c78..c0c2787 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -7,14 +7,11 @@ namespace Drupal\pathauto; -use Drupal\Component\Render\PlainTextOutput; use Drupal\Component\Utility\Unicode; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; From 335a118f852174252e4510670c5589fcfb93710b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 9 Nov 2015 23:40:56 +0100 Subject: [PATCH 101/169] Added ctools dependency for travis --- .travis-before-script.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis-before-script.sh b/.travis-before-script.sh index 21dc82f..a361750 100644 --- a/.travis-before-script.sh +++ b/.travis-before-script.sh @@ -13,5 +13,6 @@ cd "$DRUPAL_TI_DRUPAL_DIR" mkdir -p "$DRUPAL_TI_MODULES_PATH" cd "$DRUPAL_TI_MODULES_PATH" -# Download token 8.x-1.x +# Download token 8.x-1.x and ctools 8.x-3.x git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git +git clone --depth 1 --branch 8.x-3.x http://git.drupal.org/project/ctools.git From c4cfc000cbe0a34a607dcc76d2ae3b10a55cfb47 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 9 Nov 2015 23:44:25 +0100 Subject: [PATCH 102/169] Remove custom entity delete form, fix permission --- src/Entity/PathautoPattern.php | 4 +- src/Form/PathautoPatternDeleteForm.php | 57 -------------------------- 2 files changed, 2 insertions(+), 59 deletions(-) delete mode 100644 src/Form/PathautoPatternDeleteForm.php diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 77fa074..a51c5c7 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -26,7 +26,7 @@ * handlers = { * "list_builder" = "Drupal\pathauto\PathautoPatternListBuilder", * "form" = { - * "delete" = "Drupal\pathauto\Form\PathautoPatternDeleteForm" + * "delete" = "Drupal\Core\Entity\EntityDeleteForm" * }, * "wizard" = { * "add" = "Drupal\pathauto\Wizard\PatternWizardAdd", @@ -34,7 +34,7 @@ * } * }, * config_prefix = "pathauto_pattern", - * admin_permission = "administer site configuration", + * admin_permission = "administer pathauto", * entity_keys = { * "id" = "id", * "label" = "label", diff --git a/src/Form/PathautoPatternDeleteForm.php b/src/Form/PathautoPatternDeleteForm.php deleted file mode 100644 index 24a6ee8..0000000 --- a/src/Form/PathautoPatternDeleteForm.php +++ /dev/null @@ -1,57 +0,0 @@ -t('Are you sure you want to delete %name?', array('%name' => $this->entity->label())); - } - - /** - * {@inheritdoc} - */ - public function getCancelUrl() { - return new Url('entity.pathauto_pattern.collection'); - } - - /** - * {@inheritdoc} - */ - public function getConfirmText() { - return $this->t('Delete'); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $this->entity->delete(); - - drupal_set_message( - $this->t('content @type: deleted @label.', - [ - '@type' => $this->entity->bundle(), - '@label' => $this->entity->label() - ] - ) - ); - - $form_state->setRedirectUrl($this->getCancelUrl()); - } - -} From 1fd05edc79ff704b6cee3149701c68c087955c0f Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 9 Nov 2015 23:47:26 +0100 Subject: [PATCH 103/169] Shorten the config prefix to just pattern --- config/schema/pathauto_pattern.schema.yml | 2 +- src/Entity/PathautoPattern.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/schema/pathauto_pattern.schema.yml b/config/schema/pathauto_pattern.schema.yml index 6d805ba..9979a49 100644 --- a/config/schema/pathauto_pattern.schema.yml +++ b/config/schema/pathauto_pattern.schema.yml @@ -1,4 +1,4 @@ -pathauto.pathauto_pattern.*: +pathauto.pattern.*: type: config_entity label: 'Pathauto pattern config' mapping: diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index a51c5c7..4679417 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -33,7 +33,7 @@ * "edit" = "Drupal\pathauto\Wizard\PatternWizard" * } * }, - * config_prefix = "pathauto_pattern", + * config_prefix = "pattern", * admin_permission = "administer pathauto", * entity_keys = { * "id" = "id", From 9375459ef0e439021dd4d9d17661ea1630ca5a28 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 9 Nov 2015 23:48:16 +0100 Subject: [PATCH 104/169] Remove stringTranslation property in PathautoManager, duplicated by trait and causes PHP notices --- src/PathautoManager.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index c0c2787..81ee2b9 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -87,13 +87,6 @@ class PathautoManager implements PathautoManagerInterface { */ protected $messenger; - /** - * The string translation service. - * - * @var \Drupal\Core\StringTranslation\TranslationInterface - */ - protected $stringTranslation; - /** * Creates a new Pathauto manager. * From 0dd6a67fd359bcf15cd87bf032e8adae9088daf6 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 18:36:25 +0100 Subject: [PATCH 105/169] Remove default config and schema for old patterns file --- config/install/pathauto.pattern.yml | 12 ------------ config/schema/pathauto.schema.yml | 23 ----------------------- 2 files changed, 35 deletions(-) delete mode 100644 config/install/pathauto.pattern.yml diff --git a/config/install/pathauto.pattern.yml b/config/install/pathauto.pattern.yml deleted file mode 100644 index cde1de5..0000000 --- a/config/install/pathauto.pattern.yml +++ /dev/null @@ -1,12 +0,0 @@ -patterns: - node: - default: '/content/[node:title]' - - taxonomy_term: - default: '/[term:vocabulary]/[term:name]' - - forum: - default: '/[term:vocabulary]/[term:name]' - - user: - default: '/users/[user:name]' diff --git a/config/schema/pathauto.schema.yml b/config/schema/pathauto.schema.yml index 917e674..0148949 100644 --- a/config/schema/pathauto.schema.yml +++ b/config/schema/pathauto.schema.yml @@ -1,26 +1,3 @@ -pathauto.pattern: - type: config_object - mapping: - patterns: - type: sequence - sequence: - type: mapping - mapping: - default: - type: string - bundles: - type: sequence - sequence: - type: mapping - mapping: - default: - type: string - languages: - type: sequence - sequence: - type: string - - pathauto.settings: type: config_object mapping: From 9403e5543bdae0060aefdf53747bbdb0932d8518 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 18:37:22 +0100 Subject: [PATCH 106/169] Use draggable list builder, add necessary keys, remove need for custom edit url --- src/Entity/PathautoPattern.php | 21 ++++++++++++++++++++- src/PathautoPatternListBuilder.php | 29 ++++++++++++++--------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 4679417..33772e2 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -38,7 +38,8 @@ * entity_keys = { * "id" = "id", * "label" = "label", - * "uuid" = "uuid" + * "uuid" = "uuid", + * "weight" = "weight" * }, * links = { * "collection" = "/admin/config/search/path/patterns", @@ -48,6 +49,7 @@ * ) */ class PathautoPattern extends ConfigEntityBase implements PathautoPatternInterface { + /** * The Pathauto pattern ID. * @@ -149,6 +151,9 @@ public function preSave(EntityStorageInterface $storage) { } } + /** + * {@inheritdoc} + */ public static function postLoad(EntityStorageInterface $storage, array &$entities) { /** @var \Drupal\ctools\TypedDataResolver $resolver */ $resolver = \Drupal::service('ctools.typed_data.resolver'); @@ -186,6 +191,20 @@ public function calculateDependencies() { return $this->getDependencies(); } + /** + * {@inheritdoc} + */ + protected function urlRouteParameters($rel) { + $uri_route_parameters = parent::urlRouteParameters($rel); + if ($rel = 'edit-form') { + $uri_route_parameters = [ + 'machine_name' => $this->id(), + 'step' => 'general', + ]; + } + return $uri_route_parameters; + } + /** * {@inheritdoc} */ diff --git a/src/PathautoPatternListBuilder.php b/src/PathautoPatternListBuilder.php index 60aff73..269daf3 100644 --- a/src/PathautoPatternListBuilder.php +++ b/src/PathautoPatternListBuilder.php @@ -7,20 +7,28 @@ namespace Drupal\pathauto; -use Drupal\Core\Config\Entity\ConfigEntityListBuilder; +use Drupal\Core\Config\Entity\DraggableListBuilder; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Url; /** * Provides a listing of Pathauto pattern entities. */ -class PathautoPatternListBuilder extends ConfigEntityListBuilder { +class PathautoPatternListBuilder extends DraggableListBuilder { + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'pathauto_pattern_list'; + } + /** * {@inheritdoc} */ public function buildHeader() { $header['label'] = $this->t('Pathauto pattern'); $header['id'] = $this->t('Machine name'); + $header['type'] = $this->t('Pattern type'); return $header + parent::buildHeader(); } @@ -28,20 +36,11 @@ public function buildHeader() { * {@inheritdoc} */ public function buildRow(EntityInterface $entity) { - $row['label'] = $this->getLabel($entity); + /* @var \Drupal\pathauto\PathautoPatternInterface $entity */ + $row['label'] = $entity->label(); $row['id'] = $entity->id(); - // You probably want a few more properties here... + $row['type'] = $entity->getAliasType()->getLabel(); return $row + parent::buildRow($entity); } - /** - * {@inheritdoc} - */ - public function getDefaultOperations(EntityInterface $entity) { - $operations = parent::getDefaultOperations($entity); - $operations['edit']['url'] = new Url('entity.pathauto_pattern.edit_form', ['machine_name' => $entity->id(), 'step' => 'general']); - - return $operations; - } - } From e4cd27b01e9b65cff511c3bbd3ee4e6c8a4aabd9 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 18:41:08 +0100 Subject: [PATCH 107/169] Remove PathautoPatternsForm --- src/Form/PathautoAdminDelete.php | 2 +- src/Form/PathautoBulkUpdateForm.php | 2 +- src/Form/PathautoPatternsForm.php | 100 ---------------------------- 3 files changed, 2 insertions(+), 102 deletions(-) delete mode 100644 src/Form/PathautoPatternsForm.php diff --git a/src/Form/PathautoAdminDelete.php b/src/Form/PathautoAdminDelete.php index af8e302..524ad79 100644 --- a/src/Form/PathautoAdminDelete.php +++ b/src/Form/PathautoAdminDelete.php @@ -25,7 +25,7 @@ class PathautoAdminDelete extends FormBase { protected $aliasTypeManager; /** - * Constructs a PathautoPatternsForm object. + * Constructs a PathautoAdminDelete object. * * @param \Drupal\pathauto\AliasTypeManager $alias_type_manager * The alias type manager. diff --git a/src/Form/PathautoBulkUpdateForm.php b/src/Form/PathautoBulkUpdateForm.php index 925bfcb..706b18a 100644 --- a/src/Form/PathautoBulkUpdateForm.php +++ b/src/Form/PathautoBulkUpdateForm.php @@ -26,7 +26,7 @@ class PathautoBulkUpdateForm extends FormBase { protected $aliasTypeManager; /** - * Constructs a PathautoPatternsForm object. + * Constructs a PathautoBulkUpdateForm object. * * @param \Drupal\pathauto\AliasTypeManager $alias_type_manager * The alias type manager. diff --git a/src/Form/PathautoPatternsForm.php b/src/Form/PathautoPatternsForm.php deleted file mode 100644 index a2b871b..0000000 --- a/src/Form/PathautoPatternsForm.php +++ /dev/null @@ -1,100 +0,0 @@ -aliasTypeManager = $alias_type_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('config.factory'), - $container->get('plugin.manager.alias_type') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'pathauto_patterns_form'; - } - - /** - * {@inheritdoc} - */ - protected function getEditableConfigNames() { - return ['pathauto.pattern']; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - - $definitions = $this->aliasTypeManager->getDefinitions(); - - $config = $this->config('pathauto.pattern'); - - foreach ($definitions as $id => $definition) { - /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */ - $alias_type = $this->aliasTypeManager->createInstance($id, $config->get('patterns.' . $id) ?: []); - - $form[$id] = $alias_type->buildConfigurationForm([], $form_state); - } - - return parent::buildForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - - $config = $this->config('pathauto.pattern'); - - $definitions = $this->aliasTypeManager->getDefinitions(); - - foreach ($definitions as $id => $definition) { - $config->set('patterns.' . $id, $form_state->getValue($id)); - } - - $config->save(); - - parent::submitForm($form, $form_state); - } - -} From 0345fc09f1dad88664727aa728ba9faeb02e2f27 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 18:47:50 +0100 Subject: [PATCH 108/169] Remove getPatternDescription() --- src/AliasTypeInterface.php | 8 -------- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 9 +-------- src/Plugin/pathauto/AliasType/ForumAliasType.php | 7 ------- src/Plugin/pathauto/AliasType/NodeAliasType.php | 7 ------- src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php | 7 ------- src/Plugin/pathauto/AliasType/UserAliasType.php | 7 ------- 6 files changed, 1 insertion(+), 44 deletions(-) diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index 32bac49..f1bc67c 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -24,14 +24,6 @@ interface AliasTypeInterface extends ContextAwarePluginInterface, ConfigurablePl */ public function getLabel(); - /** - * Get the pattern description. - * - * @return string - * The pattern description. - */ - public function getPatternDescription(); - /** * Get the token types. * diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 3204ec7..715a690 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -152,7 +152,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form['default'] = array( '#type' => 'textfield', - '#title' => $this->getPatternDescription(), + '#title' => 'Path pattern', '#default_value' => !empty($this->configuration['default']) ? $this->configuration['default'] : '', '#size' => 65, '#maxlength' => 1280, @@ -324,13 +324,6 @@ public function applies($object) { return ($object instanceof $class); } - /** - * {@inheritdoc} - */ - public function getPatternDescription() { - return $this->t('Replace this description with proper annotation effort.'); - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index fd1bab5..0c38d4d 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -22,13 +22,6 @@ */ class ForumAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function getPatternDescription() { - return $this->t('Pattern for forums and forum containers'); - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 28859ea..db5f41f 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -22,13 +22,6 @@ */ class NodeAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function getPatternDescription() { - return $this->t('Default path pattern (applies to all content types with blank patterns below)'); - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 36ab06e..cf432c9 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -22,13 +22,6 @@ */ class TaxonomyTermAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function getPatternDescription() { - return $this->t('Default path pattern (applies to all vocabularies with blank patterns below)'); - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php index 616d594..c291c0a 100644 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ b/src/Plugin/pathauto/AliasType/UserAliasType.php @@ -22,13 +22,6 @@ */ class UserAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function getPatternDescription() { - return $this->t('Pattern for user account page paths'); - } - /** * {@inheritdoc} */ From 81a401790fa196727570575712f29ef35c20d67c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 18:48:54 +0100 Subject: [PATCH 109/169] Remove hardcoded entity alias types --- .../pathauto/AliasType/NodeAliasType.php | 39 -------------- .../AliasType/TaxonomyTermAliasType.php | 51 ------------------- .../pathauto/AliasType/UserAliasType.php | 39 -------------- 3 files changed, 129 deletions(-) delete mode 100644 src/Plugin/pathauto/AliasType/NodeAliasType.php delete mode 100644 src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php delete mode 100644 src/Plugin/pathauto/AliasType/UserAliasType.php diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php deleted file mode 100644 index db5f41f..0000000 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ /dev/null @@ -1,39 +0,0 @@ - array('/content/[node:title]')) + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function getSourcePrefix() { - return 'node/'; - } - -} diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php deleted file mode 100644 index cf432c9..0000000 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ /dev/null @@ -1,51 +0,0 @@ - array('/[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function getSourcePrefix() { - return 'taxonomy/term/'; - } - - /** - * {@inheritdoc} - */ - public function applies($object) { - if (parent::applies($object)) { - /** @var \Drupal\taxonomy\TermInterface $object */ - $config_forum = $this->configFactory->get('forum.settings'); - return $object->getVocabularyId() != $config_forum->get('vocabulary'); - } - return FALSE; - } - -} diff --git a/src/Plugin/pathauto/AliasType/UserAliasType.php b/src/Plugin/pathauto/AliasType/UserAliasType.php deleted file mode 100644 index c291c0a..0000000 --- a/src/Plugin/pathauto/AliasType/UserAliasType.php +++ /dev/null @@ -1,39 +0,0 @@ - array('/users/[user:name]')) + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function getSourcePrefix() { - return '/user/'; - } - -} From f87c390422ae879e2c2eca35fce7243b7afcdd37 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 10 Nov 2015 19:47:56 +0100 Subject: [PATCH 110/169] Remove NodeAliasTest --- src/Tests/AliasType/NodeAliasTest.php | 53 --------------------------- 1 file changed, 53 deletions(-) delete mode 100644 src/Tests/AliasType/NodeAliasTest.php diff --git a/src/Tests/AliasType/NodeAliasTest.php b/src/Tests/AliasType/NodeAliasTest.php deleted file mode 100644 index c5bfede..0000000 --- a/src/Tests/AliasType/NodeAliasTest.php +++ /dev/null @@ -1,53 +0,0 @@ -container->get('plugin.manager.alias_type'); - - /** @var \Drupal\pathauto\AliasTypeInterface $node_type */ - $node_type = $manager->createInstance('node'); - - $patterns = $node_type->getPatterns(); - $this->assertTrue((array_key_exists('node', $patterns)), "Node pattern exists."); - $this->assertEqual($patterns['node'], 'Pattern for all Content paths', "Node pattern description matches."); - - $token_types = $node_type->getTokenTypes(); - $this->assertTrue(in_array('node', $token_types), "Node token type exists."); - - $label = $node_type->getLabel(); - $this->assertEqual($label, 'Content', "Plugin label matches."); - - $default_config = $node_type->defaultConfiguration(); - - $this->assertTrue(array_key_exists('default', $default_config), "Default key exists."); - $this->assertEqual($default_config['default'][0], '/content/[node:title]', "Default content pattern matches."); - - } - -} From b01db92cef160b543673b0089fc2f22f03c2b9ec Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 11 Nov 2015 08:24:17 +0100 Subject: [PATCH 111/169] Fixed relationship url arguments, compatiblity with latest ctools, correctly use #markup in list builder --- src/Entity/PathautoPattern.php | 3 ++- src/Form/PathautoPatternForm.php | 2 +- src/PathautoPatternListBuilder.php | 4 ++-- src/Wizard/PatternWizard.php | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 33772e2..5ca7ae0 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -196,7 +196,8 @@ public function calculateDependencies() { */ protected function urlRouteParameters($rel) { $uri_route_parameters = parent::urlRouteParameters($rel); - if ($rel = 'edit-form') { + // @todo Improve detection, check the path string? + if (in_array($rel, ['edit-form', 'config-translation-overview'])) { $uri_route_parameters = [ 'machine_name' => $this->id(), 'step' => 'general', diff --git a/src/Form/PathautoPatternForm.php b/src/Form/PathautoPatternForm.php index 6dd8bf3..accbc64 100644 --- a/src/Form/PathautoPatternForm.php +++ b/src/Form/PathautoPatternForm.php @@ -56,7 +56,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $pathauto_pattern = $cached_values['pathauto_pattern']; $options = []; foreach ($this->manager->getDefinitions() as $plugin_id => $plugin_definition) { - $options[$plugin_id] = (string) $plugin_definition['label']; + $options[$plugin_id] = $plugin_definition['label']; } $form['type'] = [ '#type' => 'select', diff --git a/src/PathautoPatternListBuilder.php b/src/PathautoPatternListBuilder.php index 269daf3..87edc8e 100644 --- a/src/PathautoPatternListBuilder.php +++ b/src/PathautoPatternListBuilder.php @@ -38,8 +38,8 @@ public function buildHeader() { public function buildRow(EntityInterface $entity) { /* @var \Drupal\pathauto\PathautoPatternInterface $entity */ $row['label'] = $entity->label(); - $row['id'] = $entity->id(); - $row['type'] = $entity->getAliasType()->getLabel(); + $row['id']['#markup'] = $entity->id(); + $row['type']['#markup'] = $entity->getAliasType()->getLabel(); return $row + parent::buildRow($entity); } diff --git a/src/Wizard/PatternWizard.php b/src/Wizard/PatternWizard.php index 21f08af..aa75654 100644 --- a/src/Wizard/PatternWizard.php +++ b/src/Wizard/PatternWizard.php @@ -49,7 +49,7 @@ public function exists() { /** * {@inheritdoc} */ - public function getOperations() { + public function getOperations($cached_values) { return [ 'general' => [ 'title' => $this->t('General information'), @@ -70,4 +70,4 @@ public function getOperations() { ]; } -} \ No newline at end of file +} From 4be22f828a8527a9f0b9d50433c171f4625d3d2c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 11 Nov 2015 20:55:30 +0100 Subject: [PATCH 112/169] Remove no longer used methods from alias type plugins, fix bugs in alias type plugins, fix bulk update test --- .../AliasType/EntityAliasTypeBase.php | 68 ++----------------- .../pathauto/AliasType/ForumAliasType.php | 9 +-- src/Tests/PathautoBulkUpdateTest.php | 8 ++- src/Tests/PathautoTestHelperTrait.php | 22 ++++++ 4 files changed, 34 insertions(+), 73 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 715a690..e4ac8d8 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -14,6 +14,7 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContextAwarePluginBase; +use Drupal\pathauto\AliasTypeBatchUpdateInterface; use Drupal\pathauto\AliasTypeInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -25,7 +26,7 @@ * deriver = "\Drupal\pathauto\Plugin\Deriver\EntityAliasTypeDeriver" * ) */ -class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInterface, ContainerFactoryPluginInterface { +class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInterface, AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { /** * The module handler service. @@ -171,31 +172,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } - /** - * {@inheritdoc} - */ - public function getPatterns() { - $patterns = []; - $languages = $this->languageManager->getLanguages(); - if ($this->entityManager->getDefinition($this->getEntityTypeId())->hasKey('bundle')) { - foreach ($this->getBundles() as $bundle => $bundle_label) { - if (count($languages) && $this->isContentTranslationEnabled($bundle)) { - $patterns[$bundle] = $this->t('Default path pattern for @bundle (applies to all @bundle fields with blank patterns below)', array('@bundle' => $bundle_label)); - foreach ($languages as $language) { - $patterns[$bundle . '_' . $language->getId()] = $this->t('Pattern for all @language @bundle paths', array( - '@bundle' => $bundle_label, - '@language' => $language->getName() - )); - } - } - else { - $patterns[$bundle] = $this->t('Pattern for all @bundle paths', array('@bundle' => $bundle_label)); - } - } - } - return $patterns; - } - /** * {@inheritdoc} */ @@ -248,7 +224,7 @@ public function batchUpdate(&$context) { * The entity type ID. */ protected function getEntityTypeId() { - return $this->getPluginId(); + return $this->getDerivativeId(); } /** @@ -290,36 +266,11 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { } - /** - * Returns bundles. - * - * @return string[] - * An array of bundle labels, keyed by bundle. - */ - protected function getBundles() { - return array_map(function ($bundle_info) { - return $bundle_info['label']; - }, $this->entityManager->getBundleInfo($this->getEntityTypeId())); - } - - /** - * Checks if a bundle is enabled for translation. - * - * @param string $bundle - * The bundle. - * - * @return bool - * TRUE if content translation is enabled for the bundle. - */ - protected function isContentTranslationEnabled($bundle) { - return $this->moduleHandler->moduleExists('content_translation') && \Drupal::service('content_translation.manager')->isEnabled($this->getEntityTypeId(), $bundle); - } - /** * {@inheritdoc} */ public function applies($object) { - $definition = $this->entityManager->getDefinition($this->getDerivativeId()); + $definition = $this->entityManager->getDefinition($this->getEntityTypeId()); $class = $definition->getClass(); return ($object instanceof $class); } @@ -329,16 +280,9 @@ public function applies($object) { */ public function getSourcePrefix() { if (empty($this->prefix)) { - $entity_type = $this->entityManager->getDefinition($this->getDerivativeId()); + $entity_type = $this->entityManager->getDefinition($this->getEntityTypeId()); $path = $entity_type->getLinkTemplate('canonical'); - // Remove slug(s)... This could probably be done cleaner, but I'm not in the mood. - $path_parts = explode('/', $path); - foreach ($path_parts as $key => $value) { - if (strpos($value, '}') === 0 && strpos($value, '{') == -1) { - unset ($path_parts[$key]); - } - } - $this->prefix = implode('/', $path_parts); + $this->prefix = substr($path, 0, strpos($path, '{')); } return $this->prefix; } diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 0c38d4d..1787ca2 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -20,14 +20,7 @@ * provider = "forum", * ) */ -class ForumAliasType extends EntityAliasTypeBase implements AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface { - - /** - * {@inheritdoc} - */ - public function getPatterns() { - return []; - } +class ForumAliasType extends EntityAliasTypeBase implements ContainerFactoryPluginInterface { /** * {@inheritdoc} diff --git a/src/Tests/PathautoBulkUpdateTest.php b/src/Tests/PathautoBulkUpdateTest.php index f9f03ec..e572904 100644 --- a/src/Tests/PathautoBulkUpdateTest.php +++ b/src/Tests/PathautoBulkUpdateTest.php @@ -53,8 +53,10 @@ function setUp() { ); $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); - } + $this->createPattern('node', '/content/[node:title]'); + $this->createPattern('user', '/users/[user:name]'); + } function testBulkUpdate() { // Create some nodes. @@ -69,8 +71,8 @@ function testBulkUpdate() { // Bulk create aliases. $edit = array( - 'update[node]' => TRUE, - 'update[user]' => TRUE, + 'update[canonical_entities:node]' => TRUE, + 'update[canonical_entities:user]' => TRUE, ); $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); $this->assertText('Generated 7 URL aliases.'); // 5 nodes + 2 users diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 4dec728..c818680 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\Language; use Drupal\Core\Render\BubbleableMetadata; +use Drupal\pathauto\Entity\PathautoPattern; use Drupal\taxonomy\VocabularyInterface; /** @@ -18,6 +19,27 @@ */ trait PathautoTestHelperTrait { + /** + * Creates a pathauto pattern. + * + * @param string $entity_type_id + * The entity type. + * @param $pattern + * The path pattern. + * + * @return \Drupal\pathauto\PathautoPatternInterface + * The created pattern. + */ + protected function createPattern($entity_type_id, $pattern) { + $pattern = PathautoPattern::create([ + 'id' => Unicode::strtolower($this->randomMachineName()), + 'type' => 'canonical_entities:' . $entity_type_id, + 'pattern' => $pattern, + ]); + $pattern->save(); + return $pattern; + } + public function assertToken($type, $object, $token, $expected) { $bubbleable_metadata = new BubbleableMetadata(); $tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object), [], $bubbleable_metadata); From f5f6ec5af8de892d6ba4770cedfd970c665ea593 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 14 Nov 2015 10:27:14 +0100 Subject: [PATCH 113/169] Starting fixing tests --- config/schema/pathauto_pattern.schema.yml | 4 +- src/Form/ConfigurePatternForm.php | 5 + src/Tests/PathautoLocaleTest.php | 2 + src/Tests/PathautoUnitTest.php | 106 +++++++++++++++++----- 4 files changed, 92 insertions(+), 25 deletions(-) diff --git a/config/schema/pathauto_pattern.schema.yml b/config/schema/pathauto_pattern.schema.yml index 9979a49..1cadb39 100644 --- a/config/schema/pathauto_pattern.schema.yml +++ b/config/schema/pathauto_pattern.schema.yml @@ -20,8 +20,8 @@ pathauto.pattern.*: type: sequence label: 'Selection criteria' sequence: - - type: condition.plugin.[id] - label: 'Selection condition' + type: condition.plugin.[id] + label: 'Selection condition' selection_logic: type: string label: 'Selection logic' diff --git a/src/Form/ConfigurePatternForm.php b/src/Form/ConfigurePatternForm.php index be4e47d..db6dc45 100644 --- a/src/Form/ConfigurePatternForm.php +++ b/src/Form/ConfigurePatternForm.php @@ -36,6 +36,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { $tokens = $form['default']['#token_types']; $contexts = $pathauto_pattern->getContexts(); foreach ($contexts as $context_id => $context) { + // @todo Figure out what to do with non-entity contexts. + if (strpos($context->getContextDefinition()->getDataType(), ':') === FALSE) { + continue; + } list($data_type, $entity_type) = explode(':', $context->getContextDefinition()->getDataType()); if ($data_type == 'entity') { if ($entity_type == 'taxonomy_term') { @@ -46,6 +50,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { } } } + $form['default']['#token_types'] = $tokens; return $form; diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 1a1d4f9..7b95092 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -41,6 +41,8 @@ class PathautoLocaleTest extends WebTestBase { */ function testLanguageAliases() { + $this->createPattern('node', '/content/[node:title]'); + // Add predefined French language. ConfigurableLanguage::createFromLangcode('fr')->save(); diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 4925ae7..94e5fc0 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageInterface; use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; use Drupal\simpletest\KernelTestBase; @@ -22,7 +23,7 @@ class PathautoUnitTest extends KernelTestBase { use PathautoTestHelperTrait; - public static $modules = array('system', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'filter'); + public static $modules = array('system', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'filter', 'ctools', 'language'); protected $currentUser; @@ -42,6 +43,9 @@ public function setUp() { $type->save(); node_add_body_field($type); + $this->createPattern('node', '/content/[node:title]'); + $this->createPattern('user', '/users/[user:name]'); + \Drupal::service('router.builder')->rebuild(); $this->currentUser = entity_create('user', array('name' => $this->randomMachineName())); @@ -59,52 +63,108 @@ public function testGetSchemaAliasMaxLength() { * Test pathauto_pattern_load_by_entity(). */ public function testPatternLoadByEntity() { - $this->config('pathauto.pattern') - ->set('patterns.node.bundles.article.default', '/article/[node:title]') - ->set('patterns.node.bundles.article.languages.en', '/article/en/[node:title]') - ->set('patterns.node.bundles.page.default', '/[node:title]') - ->save(); + $pattern = $this->createPattern('node', '/article/[node:title]'); + $pattern->addSelectionCondition( + [ + 'id' => 'entity_type:node_type', + 'bundles' => [ + 'article' => 'article', + ], + 'negate' => FALSE, + 'context_mapping' => [ + 'node' => 'node', + ] + ] + ); + $pattern->setWeight(-1); + $pattern->save(); + + $pattern = $this->createPattern('node', '/article/[node:title]'); + $pattern->addSelectionCondition( + [ + 'id' => 'entity_type:node_type', + 'bundles' => [ + 'article' => 'article', + ], + 'negate' => FALSE, + 'context_mapping' => [ + 'node' => 'node', + ] + ] + ); + $pattern->addSelectionCondition( + [ + 'id' => 'language', + 'langcodes' => [ + 'fr' => 'fr', + ], + 'negate' => FALSE, + 'context_mapping' => [ + 'language' => 'node:langcode', + ] + ] + ); + $pattern->set('context_definitions', [['id' => 'node:langcode', 'label' => 'Language']]); + $pattern->setWeight(-2); + $pattern->save(); + + $pattern = $this->createPattern('node', '/article/[node:title]'); + $pattern->addSelectionCondition( + [ + 'id' => 'entity_type:node_type', + 'bundles' => [ + 'page' => 'page', + ], + 'negate' => FALSE, + 'context_mapping' => [ + 'node' => 'node', + ] + ] + ); + $pattern->setWeight(1); + $pattern->save(); $tests = array( array( 'entity' => 'node', - 'bundle' => 'article', - 'language' => 'fr', + 'values' => [ + 'type' => 'article', + 'langcode' => 'fr', + ], 'expected' => '/article/[node:title]', ), array( 'entity' => 'node', - 'bundle' => 'article', - 'language' => 'en', + 'values' => [ + 'type' => 'article', + 'langcode' => 'en', + ], 'expected' => '/article/en/[node:title]', ), array( 'entity' => 'node', - 'bundle' => 'article', - 'language' => Language::LANGCODE_NOT_SPECIFIED, + 'values' => [ + 'type' => 'article', + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ], 'expected' => '/article/[node:title]', ), array( 'entity' => 'node', - 'bundle' => 'page', - 'language' => 'en', + 'values' => [ + 'type' => 'page', + ], 'expected' => '/[node:title]', ), array( 'entity' => 'user', - 'bundle' => 'user', - 'language' => Language::LANGCODE_NOT_SPECIFIED, + 'values' => [], 'expected' => '/users/[user:name]', ), - array( - 'entity' => 'invalid-entity', - 'bundle' => '', - 'language' => Language::LANGCODE_NOT_SPECIFIED, - 'expected' => '', - ), ); foreach ($tests as $test) { - $actual = \Drupal::service('pathauto.manager')->getPatternByEntity($test['entity'], $test['bundle'], $test['language']); + $entity = \Drupal::entityManager()->getStorage($test['entity'])->create($test['values']); + $actual = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); $this->assertIdentical($actual, $test['expected'], t("pathauto_pattern_load_by_entity('@entity', '@bundle', '@language') returned '@actual', expected '@expected'", array( '@entity' => $test['entity'], '@bundle' => $test['bundle'], From 0f34a98f0daaca24c5f5edeccdcf26181bb3bbd6 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 14 Nov 2015 13:27:14 +0100 Subject: [PATCH 114/169] PathautoUnitTest almost working again --- src/Entity/PathautoPattern.php | 1 + src/PathautoPatternInterface.php | 2 + src/Tests/PathautoTestHelperTrait.php | 11 +++- src/Tests/PathautoUnitTest.php | 88 +++++++++++++++++++-------- 4 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 5ca7ae0..afd5bb3 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -219,6 +219,7 @@ public function getPattern() { public function setPattern($pattern) { $this->pattern = $pattern; $this->getAliasType()->setConfiguration(['default' => $pattern]); + return $this; } /** diff --git a/src/PathautoPatternInterface.php b/src/PathautoPatternInterface.php index c716ece..33d67d2 100644 --- a/src/PathautoPatternInterface.php +++ b/src/PathautoPatternInterface.php @@ -26,6 +26,8 @@ public function getPattern(); * Set the tokenized pattern to use during alias generation. * * @param string $pattern + * + * @return $this */ public function setPattern($pattern); diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index c818680..141162e 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -141,8 +141,15 @@ public function addTerm(VocabularyInterface $vocabulary, array $values = array() public function assertEntityPattern($entity_type, $bundle, $langcode = Language::LANGCODE_NOT_SPECIFIED, $expected) { \Drupal::service('pathauto.manager')->resetCaches(); - $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity_type, $bundle, $langcode); - $this->assertIdentical($expected, $pattern); + + $values = [ + 'langcode' => $langcode, + \Drupal::entityTypeManager()->getDefinition($entity_type)->getKey('bundle') => $bundle, + ]; + $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create($values); + + $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); + $this->assertIdentical($expected, $pattern->getPattern()); } public function drupalGetTermByName($name, $reset = FALSE) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 94e5fc0..e6731e4 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -10,6 +10,9 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; use Drupal\simpletest\KernelTestBase; @@ -27,6 +30,16 @@ class PathautoUnitTest extends KernelTestBase { protected $currentUser; + /** + * @var \Drupal\pathauto\PathautoPatternInterface + */ + protected $nodePattern; + + /** + * @var \Drupal\pathauto\PathautoPatternInterface + */ + protected $userPattern; + public function setUp() { parent::setup(); @@ -36,6 +49,8 @@ public function setUp() { $this->installEntitySchema('node'); $this->installEntitySchema('taxonomy_term'); + ConfigurableLanguage::createFromLangcode('fr')->save(); + $this->installSchema('node', array('node_access')); $this->installSchema('system', array('url_alias', 'sequences', 'router')); @@ -43,8 +58,8 @@ public function setUp() { $type->save(); node_add_body_field($type); - $this->createPattern('node', '/content/[node:title]'); - $this->createPattern('user', '/users/[user:name]'); + $this->nodePattern = $this->createPattern('node', '/content/[node:title]'); + $this->userPattern = $this->createPattern('user', '/users/[user:name]'); \Drupal::service('router.builder')->rebuild(); @@ -79,7 +94,7 @@ public function testPatternLoadByEntity() { $pattern->setWeight(-1); $pattern->save(); - $pattern = $this->createPattern('node', '/article/[node:title]'); + $pattern = $this->createPattern('node', '/article/en/[node:title]'); $pattern->addSelectionCondition( [ 'id' => 'entity_type:node_type', @@ -96,19 +111,22 @@ public function testPatternLoadByEntity() { [ 'id' => 'language', 'langcodes' => [ - 'fr' => 'fr', + 'en' => 'en', ], 'negate' => FALSE, 'context_mapping' => [ - 'language' => 'node:langcode', + 'language' => 'node:langcode:language', ] ] ); - $pattern->set('context_definitions', [['id' => 'node:langcode', 'label' => 'Language']]); + + $new_definition = new ContextDefinition('language', 'Language'); + $new_context = new Context($new_definition); + $pattern->addContext('node:langcode:language', $new_context); $pattern->setWeight(-2); $pattern->save(); - $pattern = $this->createPattern('node', '/article/[node:title]'); + $pattern = $this->createPattern('node', '/[node:title]'); $pattern->addSelectionCondition( [ 'id' => 'entity_type:node_type', @@ -121,13 +139,14 @@ public function testPatternLoadByEntity() { ] ] ); - $pattern->setWeight(1); + $pattern->setWeight(-1); $pattern->save(); $tests = array( array( 'entity' => 'node', 'values' => [ + 'title' => 'Article fr', 'type' => 'article', 'langcode' => 'fr', ], @@ -136,6 +155,7 @@ public function testPatternLoadByEntity() { array( 'entity' => 'node', 'values' => [ + 'title' => 'Article en', 'type' => 'article', 'langcode' => 'en', ], @@ -144,6 +164,7 @@ public function testPatternLoadByEntity() { array( 'entity' => 'node', 'values' => [ + 'title' => 'Article und', 'type' => 'article', 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, ], @@ -152,25 +173,26 @@ public function testPatternLoadByEntity() { array( 'entity' => 'node', 'values' => [ + 'title' => 'Page', 'type' => 'page', ], 'expected' => '/[node:title]', ), array( 'entity' => 'user', - 'values' => [], + 'values' => [ + 'name' => 'User', + ], 'expected' => '/users/[user:name]', ), ); foreach ($tests as $test) { $entity = \Drupal::entityManager()->getStorage($test['entity'])->create($test['values']); + $entity->save(); $actual = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); - $this->assertIdentical($actual, $test['expected'], t("pathauto_pattern_load_by_entity('@entity', '@bundle', '@language') returned '@actual', expected '@expected'", array( + $this->assertIdentical($actual->getPattern(), $test['expected'], t("Correct pattern returned for @entity_type with @values", array( '@entity' => $test['entity'], - '@bundle' => $test['bundle'], - '@language' => $test['language'], - '@actual' => $actual, - '@expected' => $test['expected'], + '@values' => print_r($test['values'], TRUE), ))); } } @@ -316,9 +338,9 @@ public function testUpdateActions() { */ public function testNoTokensNoAlias() { $this->installConfig(['filter']); - $config = $this->config('pathauto.pattern'); - $config->set('patterns.node.default', '/content/[node:body]'); - $config->save(); + $this->nodePattern + ->setPattern('/content/[node:body]') + ->save(); $node = $this->drupalCreateNode(); $this->assertNoEntityAliasExists($node); @@ -332,9 +354,7 @@ public function testNoTokensNoAlias() { * Test the handling of path vs non-path tokens in pathauto_clean_token_values(). */ public function testPathTokens() { - $config = $this->config('pathauto.pattern'); - $config->set('patterns.taxonomy_term.default', '/[term:parent:url:path]/[term:name]'); - $config->save(); + $this->createPattern('taxonomy_term', '/[term:parent:url:path]/[term:name]'); $vocab = $this->addVocabulary(); @@ -350,13 +370,25 @@ public function testPathTokens() { } public function testEntityBundleDeleting() { - $config = $this->config('pathauto.pattern'); - // Create a vocabulary and test that it's pattern variable works. $vocab = $this->addVocabulary(array('vid' => 'name')); - $config->set('patterns.taxonomy_term.default', 'base'); - $config->set('patterns.taxonomy_term.bundles.name.default', 'bundle'); - $config->save(); + $this->createPattern('taxonomy_term', 'base'); + $pattern = $this->createPattern('taxonomy_term', 'bundle'); + $pattern->addSelectionCondition( + [ + 'id' => 'entity_type:taxonomy_vocabulary', + 'bundles' => [ + 'name' => 'name', + ], + 'negate' => FALSE, + 'context_mapping' => [ + 'taxonomy_term' => 'taxonomy_term', + ] + ] + ); + $pattern->setWeight(1); + $pattern->save(); + $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'bundle'); @@ -371,8 +403,9 @@ function testNoExistingPathAliases() { $this->config('pathauto.settings') ->set('punctuation.period', PathautoManagerInterface::PUNCTUATION_DO_NOTHING) ->save(); - $this->config('pathauto.pattern') - ->set('patterns.node.bundles.page.default', '[node:title]') + + $this->nodePattern + ->setPattern('[node:title]') ->save(); \Drupal::service('pathauto.manager')->resetCaches(); @@ -401,6 +434,7 @@ function testNoExistingPathAliases() { * Test programmatic entity creation for aliases. */ function testProgrammaticEntityCreation() { + $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]'); $node = $this->drupalCreateNode(array('title' => 'Test node', 'path' => array('pathauto' => TRUE))); $this->assertEntityAlias($node, '/content/test-node'); From 72872c4d0d006d2e0f34ce5157ebbdb82810ce33 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 14 Nov 2015 13:39:53 +0100 Subject: [PATCH 115/169] Started fixing web tests --- pathauto.module | 1 + src/Tests/PathautoNodeWebTest.php | 13 ++++++++++--- src/Tests/PathautoTaxonomyWebTest.php | 2 ++ src/Tests/PathautoUserWebTest.php | 2 ++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pathauto.module b/pathauto.module index 0149e40..29a1aaa 100644 --- a/pathauto.module +++ b/pathauto.module @@ -64,6 +64,7 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { * Implements hook_entity_bundle_delete(). */ function pathauto_entity_bundle_delete($entity_type, $bundle) { + // @todo Update or remove this. $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); $config->clear('patterns.' . $entity_type . '.bundles.' . $bundle); $config->save(); diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index eb68e9e..6dcd6b7 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\pathauto\Tests; +use Drupal\pathauto\Entity\PathautoPattern; use Drupal\simpletest\WebTestBase; /** @@ -51,6 +52,9 @@ function setUp() { ); $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); + + $this->createPattern('node', '/content/[node:title]'); + } /** @@ -116,9 +120,12 @@ function testNodeEditing() { // Remove the pattern for nodes, the pathauto checkbox should not be // displayed. - $config = $this->config('pathauto.pattern'); - $config->set('patterns.node.default', ''); - $config->save(); + $ids = \Drupal::entityQuery('pathauto_pattern') + ->condition('type', 'canonical_entities:node') + ->execute(); + foreach (PathautoPattern::loadMultiple($ids) as $pattern) { + $pattern->delete(); + } \Drupal::service('pathauto.manager')->resetCaches(); $this->drupalGet('node/add/article'); diff --git a/src/Tests/PathautoTaxonomyWebTest.php b/src/Tests/PathautoTaxonomyWebTest.php index c1640c1..12da07f 100644 --- a/src/Tests/PathautoTaxonomyWebTest.php +++ b/src/Tests/PathautoTaxonomyWebTest.php @@ -46,6 +46,8 @@ function setUp() { ); $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); + + $this->createPattern('taxonomy_term', '/[term:parent:url:path]/[term:name]'); } diff --git a/src/Tests/PathautoUserWebTest.php b/src/Tests/PathautoUserWebTest.php index bef28f4..a2b30f5 100644 --- a/src/Tests/PathautoUserWebTest.php +++ b/src/Tests/PathautoUserWebTest.php @@ -48,6 +48,8 @@ function setUp() { ); $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); + + $this->createPattern('user', '/users/[user:name]'); } From 732c65cfd168da5f8faea6876da7c98c945dd6e7 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Tue, 17 Nov 2015 13:22:55 +0100 Subject: [PATCH 116/169] Update the source prefix for the forum, node and taxonomy term alias types. --- src/Plugin/pathauto/AliasType/ForumAliasType.php | 2 +- src/Plugin/pathauto/AliasType/NodeAliasType.php | 2 +- src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 8b88921..c08cace 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -54,7 +54,7 @@ protected function getEntityTypeId() { * {@inheritdoc} */ public function getSourcePrefix() { - return 'forum/'; + return '/forum/'; } } diff --git a/src/Plugin/pathauto/AliasType/NodeAliasType.php b/src/Plugin/pathauto/AliasType/NodeAliasType.php index 28859ea..2f9baca 100644 --- a/src/Plugin/pathauto/AliasType/NodeAliasType.php +++ b/src/Plugin/pathauto/AliasType/NodeAliasType.php @@ -40,7 +40,7 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function getSourcePrefix() { - return 'node/'; + return '/node/'; } } diff --git a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php index 448d2e4..bdff4bc 100644 --- a/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php +++ b/src/Plugin/pathauto/AliasType/TaxonomyTermAliasType.php @@ -40,7 +40,7 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function getSourcePrefix() { - return 'taxonomy/term/'; + return '/taxonomy/term/'; } } From bb0da8f3540c767b28eae2b000d870933841d9ee Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 18 Nov 2015 00:05:42 +0100 Subject: [PATCH 117/169] Rename createAlias() and updateAlias() methods --- pathauto.module | 4 ++-- src/PathautoManager.php | 8 ++++---- src/PathautoManagerInterface.php | 4 ++-- src/PathautoWidget.php | 2 +- src/Plugin/Action/UpdateAction.php | 2 +- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 2 +- src/Tests/PathautoUnitTest.php | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pathauto.module b/pathauto.module index 29a1aaa..ff47815 100644 --- a/pathauto.module +++ b/pathauto.module @@ -103,14 +103,14 @@ function pathauto_entity_presave($entity) { * Implements hook_entity_insert(). */ function pathauto_entity_insert(EntityInterface $entity) { - \Drupal::service('pathauto.manager')->updateAlias($entity, 'insert'); + \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'insert'); } /** * Implements hook_entity_update(). */ function pathauto_entity_update(EntityInterface $entity) { - \Drupal::service('pathauto.manager')->updateAlias($entity, 'update'); + \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'update'); } diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 81ee2b9..e226414 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -121,7 +121,7 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle /** * {@inheritdoc} */ - public function createAlias(EntityInterface $entity, $op) { + public function createEntityAlias(EntityInterface $entity, $op) { // Retrieve and apply the pattern for this content type. $pattern = $this->getPatternByEntity($entity); if (empty($pattern)) { @@ -266,7 +266,7 @@ public function resetCaches() { /** * {@inheritdoc} */ - public function updateAlias(EntityInterface $entity, $op, array $options = array()) { + public function updateEntityAlias(EntityInterface $entity, $op, array $options = array()) { // Skip if the entity does not have the path field. if (!($entity instanceof ContentEntityInterface) || !$entity->hasField('path')) { return NULL; @@ -294,14 +294,14 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } } - $result = $this->createAlias($entity, $op); + $result = $this->createEntityAlias($entity, $op); if ($type == 'taxonomy_term' && empty($options['is_child'])) { // For all children generate new aliases. $options['is_child'] = TRUE; unset($options['language']); foreach ($this->getTermTree($entity->getVocabularyId(), $entity->id(), NULL, TRUE) as $subterm) { - $this->updateAlias($subterm, $op, $options); + $this->updateEntityAlias($subterm, $op, $options); } } diff --git a/src/PathautoManagerInterface.php b/src/PathautoManagerInterface.php index 1728379..86e30cb 100644 --- a/src/PathautoManagerInterface.php +++ b/src/PathautoManagerInterface.php @@ -72,7 +72,7 @@ public function getPatternByEntity(EntityInterface $entity); * * @see _pathauto_set_alias() */ - public function createAlias(EntityInterface $entity, $op); + public function createEntityAlias(EntityInterface $entity, $op); /** * Creates or updates an alias for the given entity. @@ -89,6 +89,6 @@ public function createAlias(EntityInterface $entity, $op); * - An array with alias data in case the alias has been created or updated. * - NULL if no operation performed. */ - public function updateAlias(EntityInterface $entity, $op, array $options = array()); + public function updateEntityAlias(EntityInterface $entity, $op, array $options = array()); } diff --git a/src/PathautoWidget.php b/src/PathautoWidget.php index 2cc889e..61f62db 100644 --- a/src/PathautoWidget.php +++ b/src/PathautoWidget.php @@ -53,7 +53,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if (!$entity->isNew()) { module_load_include('inc', 'pathauto'); $path = \Drupal::service('path.alias_manager')->getAliasByPath('/' . $entity->urlInfo()->getInternalPath(), $entity->language()->getId()); - $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity, 'return'); + $pathauto_alias = \Drupal::service('pathauto.manager')->createEntityAlias($entity, 'return'); $entity->path->pathauto = ($path != '/' . $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); } else { diff --git a/src/Plugin/Action/UpdateAction.php b/src/Plugin/Action/UpdateAction.php index fd7b102..95caaef 100644 --- a/src/Plugin/Action/UpdateAction.php +++ b/src/Plugin/Action/UpdateAction.php @@ -26,7 +26,7 @@ class UpdateAction extends ActionBase { public function execute($entity = NULL) { $entity->path = new \stdClass(); $entity->path->pathauto = TRUE; - \Drupal::service('pathauto.manager')->updateAlias($entity, 'bulkupdate', array('message' => TRUE)); + \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'bulkupdate', array('message' => TRUE)); } /** diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index e4ac8d8..3bb3f8a 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -240,7 +240,7 @@ protected function bulkUpdate(array $ids, array $options = array()) { $entities = $this->entityManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); foreach ($entities as $entity) { - \Drupal::service('pathauto.manager')->updateAlias($entity, 'bulkupdate', $options); + \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'bulkupdate', $options); } if (!empty($options['message'])) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index e6731e4..118229a 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -274,7 +274,7 @@ public function testPathDeleteMultiple() { } /** - * Test the different update actions in \Drupal::service('pathauto.manager')->createAlias(). + * Test the different update actions in \Drupal::service('pathauto.manager')->createEntityAlias(). */ public function testUpdateActions() { $config = $this->config('pathauto.settings'); @@ -328,12 +328,12 @@ public function testUpdateActions() { // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'. $this->deleteAllAliases(); $node->setTitle('Sixth title'); - \Drupal::service('pathauto.manager')->updateAlias($node, 'bulkupdate'); + \Drupal::service('pathauto.manager')->updateEntityAlias($node, 'bulkupdate'); $this->assertEntityAlias($node, '/content/sixth-title'); } /** - * Test that \Drupal::service('pathauto.manager')->createAlias() will not create an alias for a pattern + * Test that \Drupal::service('pathauto.manager')->createEntityAlias() will not create an alias for a pattern * that does not get any tokens replaced. */ public function testNoTokensNoAlias() { From 9699d7c1209adba0ff7b83ae3ab86c749b09448d Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 18 Nov 2015 00:05:50 +0100 Subject: [PATCH 118/169] Documentation and cleanup --- pathauto.routing.yml | 2 +- src/AliasTypeManager.php | 4 ++++ src/Form/PathautoSettingsForm.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pathauto.routing.yml b/pathauto.routing.yml index c2c123e..d280ba4 100644 --- a/pathauto.routing.yml +++ b/pathauto.routing.yml @@ -30,7 +30,7 @@ entity.pathauto_pattern.delete_form: _entity_form: 'pathauto_pattern.delete' _title: 'Delete Pathauto pattern' requirements: - _permission: 'administer pathauto' + _entity_access: pathauto_pattern.delete pathauto.pattern.relationship.add: path: '/admin/config/search/path/patterns/{machine_name}/relationship/{context}/add' diff --git a/src/AliasTypeManager.php b/src/AliasTypeManager.php index fc54f69..b7b459b 100644 --- a/src/AliasTypeManager.php +++ b/src/AliasTypeManager.php @@ -34,9 +34,13 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac } /** + * Returns plugin definitions that support a given token type. + * * @param string $type * The type of token plugin must support to be useful. + * * @return array + * Plugin definitions. */ public function getPluginDefinitionByType($type) { $definitions = array_filter($this->getDefinitions(), function ($definition) use ($type) { diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 3e2e1c9..7d71308 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -188,7 +188,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function submitForm(array &$form, FormStateInterface $form_state) { - $config = $this->configFactory()->getEditable('pathauto.settings'); + $config = $this->config('pathauto.settings'); $form_state->cleanValues(); foreach ($form_state->getValues() as $key => $value) { From 8acd98cb715eeb4c73c9c7dd40c8c981656188a2 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 18 Nov 2015 00:19:51 +0100 Subject: [PATCH 119/169] Rename PathautoManager to PathautoGenerator --- pathauto.services.yml | 2 +- src/AliasCleaner.php | 6 ++--- src/AliasStorageHelper.php | 6 ++--- src/Form/PathautoSettingsForm.php | 18 ++++++------- ...hautoManager.php => PathautoGenerator.php} | 25 ++++++++----------- ...ace.php => PathautoGeneratorInterface.php} | 6 ++--- src/Tests/PathautoUnitTest.php | 14 +++++------ 7 files changed, 36 insertions(+), 41 deletions(-) rename src/{PathautoManager.php => PathautoGenerator.php} (94%) rename src/{PathautoManagerInterface.php => PathautoGeneratorInterface.php} (93%) diff --git a/pathauto.services.yml b/pathauto.services.yml index e2a6897..7b17c15 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -1,6 +1,6 @@ services: pathauto.manager: - class: Drupal\pathauto\PathautoManager + class: Drupal\pathauto\PathautoGenerator arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation'] pathauto.alias_cleaner: class: Drupal\pathauto\AliasCleaner diff --git a/src/AliasCleaner.php b/src/AliasCleaner.php index f6dc672..c5df05c 100644 --- a/src/AliasCleaner.php +++ b/src/AliasCleaner.php @@ -180,15 +180,15 @@ public function cleanString($string, array $options = array()) { foreach ($punctuation as $name => $details) { $action = $config->get('punctuation.' . $name); switch ($action) { - case PathautoManagerInterface::PUNCTUATION_REMOVE: + case PathautoGeneratorInterface::PUNCTUATION_REMOVE: $cache['punctuation'][$details['value']] = ''; $this->cleanStringCache; - case PathautoManagerInterface::PUNCTUATION_REPLACE: + case PathautoGeneratorInterface::PUNCTUATION_REPLACE: $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator']; break; - case PathautoManagerInterface::PUNCTUATION_DO_NOTHING: + case PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING: // Literally do nothing. break; } diff --git a/src/AliasStorageHelper.php b/src/AliasStorageHelper.php index 7114400..593c067 100644 --- a/src/AliasStorageHelper.php +++ b/src/AliasStorageHelper.php @@ -110,16 +110,16 @@ public function save(array $path, $existing_alias = NULL, $op = NULL) { // If there is already an alias, respect some update actions. if (!empty($existing_alias)) { switch ($config->get('update_action')) { - case PathautoManagerInterface::UPDATE_ACTION_NO_NEW: + case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW: // Do not create the alias. return NULL; - case PathautoManagerInterface::UPDATE_ACTION_LEAVE: + case PathautoGeneratorInterface::UPDATE_ACTION_LEAVE: // Create a new alias instead of overwriting the existing by leaving // $path['pid'] empty. break; - case PathautoManagerInterface::UPDATE_ACTION_DELETE: + case PathautoGeneratorInterface::UPDATE_ACTION_DELETE: // The delete actions should overwrite the existing alias. $path['pid'] = $existing_alias['pid']; break; diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 7d71308..2ca043b 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Url; -use Drupal\pathauto\PathautoManagerInterface; +use Drupal\pathauto\PathautoGeneratorInterface; use Drupal\Core\Form\FormStateInterface; /** @@ -119,9 +119,9 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#title' => t('Update action'), '#default_value' => $config->get('update_action'), '#options' => array( - PathautoManagerInterface::UPDATE_ACTION_NO_NEW => t('Do nothing. Leave the old alias intact.'), - PathautoManagerInterface::UPDATE_ACTION_LEAVE => t('Create a new alias. Leave the existing alias functioning.'), - PathautoManagerInterface::UPDATE_ACTION_DELETE => t('Create a new alias. Delete the old alias.'), + PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW => t('Do nothing. Leave the old alias intact.'), + PathautoGeneratorInterface::UPDATE_ACTION_LEAVE => t('Create a new alias. Leave the existing alias functioning.'), + PathautoGeneratorInterface::UPDATE_ACTION_DELETE => t('Create a new alias. Delete the old alias.'), ), '#description' => $description, ); @@ -160,22 +160,22 @@ public function buildForm(array $form, FormStateInterface $form_state) { foreach ($punctuation as $name => $details) { if (!$config->get('punctuation.punctuation'. $name)) { - $details['default'] = PathautoManagerInterface::PUNCTUATION_REMOVE; + $details['default'] = PathautoGeneratorInterface::PUNCTUATION_REMOVE; } else { $details['default'] = $config->get('punctuation.punctuation'. $name); } if ($details['value'] == $config->get('separator')) { - $details['default'] = PathautoManagerInterface::PUNCTUATION_REPLACE; + $details['default'] = PathautoGeneratorInterface::PUNCTUATION_REPLACE; } $form['punctuation']['punctuation' . $name] = array( '#type' => 'select', '#title' => $details['name'] . ' (' . SafeMarkup::checkPlain($details['value']) . ')', '#default_value' => $details['default'], '#options' => array( - PathautoManagerInterface::PUNCTUATION_REMOVE => t('Remove'), - PathautoManagerInterface::PUNCTUATION_REPLACE => t('Replace by separator'), - PathautoManagerInterface::PUNCTUATION_DO_NOTHING => t('No action (do not replace)'), + PathautoGeneratorInterface::PUNCTUATION_REMOVE => t('Remove'), + PathautoGeneratorInterface::PUNCTUATION_REPLACE => t('Replace by separator'), + PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING => t('No action (do not replace)'), ), ); } diff --git a/src/PathautoManager.php b/src/PathautoGenerator.php similarity index 94% rename from src/PathautoManager.php rename to src/PathautoGenerator.php index e226414..62b61e0 100644 --- a/src/PathautoManager.php +++ b/src/PathautoGenerator.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\pathauto\PathautoManager. + * Contains \Drupal\pathauto\PathautoGenerator. */ namespace Drupal\pathauto; @@ -18,9 +18,9 @@ use Drupal\Core\Utility\Token; /** - * Provides methods for managing pathauto aliases and related entities. + * Provides methods for generating path aliases. */ -class PathautoManager implements PathautoManagerInterface { +class PathautoGenerator implements PathautoGeneratorInterface { use StringTranslationTrait; @@ -129,29 +129,24 @@ public function createEntityAlias(EntityInterface $entity, $op) { return NULL; } - $entity_type_id = $entity->getEntityTypeId(); - $source = $entity->urlInfo()->toString(); + $source = '/' . $entity->urlInfo()->getInternalPath(); $config = $this->configFactory->get('pathauto.settings'); $langcode = $entity->language()->getId(); - if ($entity->getEntityType()->getBundleEntityType()) { - $type = $entity->bundle(); - } - else { - $type = NULL; - } + $data = [ - $entity_type_id => $entity + $entity->getEntityTypeId() => $entity ]; // Allow other modules to alter the pattern. $context = array( - 'module' => $entity_type_id, + 'module' => $entity->getEntityType()->getProvider(), 'op' => $op, 'source' => $source, 'data' => $data, - 'type' => $type, + 'bundle' => $entity->bundle(), 'language' => &$langcode, ); + // @todo Is still hook still useful? $this->moduleHandler->alter('pathauto_pattern', $pattern, $context); // Special handling when updating an item which is already aliased. @@ -159,7 +154,7 @@ public function createEntityAlias(EntityInterface $entity, $op) { if ($op == 'update' || $op == 'bulkupdate') { if ($existing_alias = $this->aliasStorageHelper->loadBySource($source, $langcode)) { switch ($config->get('update_action')) { - case PathautoManagerInterface::UPDATE_ACTION_NO_NEW: + case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW: // If an alias already exists, // and the update action is set to do nothing, // then gosh-darn it, do nothing. diff --git a/src/PathautoManagerInterface.php b/src/PathautoGeneratorInterface.php similarity index 93% rename from src/PathautoManagerInterface.php rename to src/PathautoGeneratorInterface.php index 86e30cb..057692e 100644 --- a/src/PathautoManagerInterface.php +++ b/src/PathautoGeneratorInterface.php @@ -1,7 +1,7 @@ config('pathauto.settings'); // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'insert'. - $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); + $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW); $config->save(); $node = $this->drupalCreateNode(array('title' => 'First title')); $this->assertEntityAlias($node, '/content/first-title'); @@ -288,7 +288,7 @@ public function testUpdateActions() { $node->path->pathauto = TRUE; // Default action is PATHAUTO_UPDATE_ACTION_DELETE. - $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); + $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE); $config->save(); $node->setTitle('Second title'); $node->save(); @@ -296,14 +296,14 @@ public function testUpdateActions() { $this->assertNoAliasExists(array('alias' => '/content/first-title')); // Test PATHAUTO_UPDATE_ACTION_LEAVE - $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_LEAVE); + $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_LEAVE); $config->save(); $node->setTitle('Third title'); $node->save(); $this->assertEntityAlias($node, '/content/third-title'); $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); - $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); + $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE); $config->save(); $node->setTitle('Fourth title'); $node->save(); @@ -313,7 +313,7 @@ public function testUpdateActions() { $older_path = $this->assertAliasExists(array('source' => '/' . $node->urlInfo()->getInternalPath(), 'alias' => '/content/second-title')); \Drupal::service('path.alias_storage')->delete($older_path); - $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_NO_NEW); + $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW); $config->save(); $node->setTitle('Fifth title'); $node->save(); @@ -401,7 +401,7 @@ public function testEntityBundleDeleting() { function testNoExistingPathAliases() { $this->config('pathauto.settings') - ->set('punctuation.period', PathautoManagerInterface::PUNCTUATION_DO_NOTHING) + ->set('punctuation.period', PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING) ->save(); $this->nodePattern From 217afbbbf565c1a069bb828f5af5351dd6c5c704 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 18 Nov 2015 00:36:09 +0100 Subject: [PATCH 120/169] Refactor pattern matching and loading --- src/Entity/PathautoPattern.php | 3 ++ src/PathautoGenerator.php | 65 ++++++++++++++++++------------ src/PathautoGeneratorInterface.php | 3 +- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index afd5bb3..47ba165 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -41,6 +41,9 @@ * "uuid" = "uuid", * "weight" = "weight" * }, + * lookup_keys = { + * "type", + * }, * links = { * "collection" = "/admin/config/search/path/patterns", * "edit-form" = "/admin/config/search/path/patterns/{machine_name}/{step}", diff --git a/src/PathautoGenerator.php b/src/PathautoGenerator.php index 62b61e0..2d7642a 100644 --- a/src/PathautoGenerator.php +++ b/src/PathautoGenerator.php @@ -24,13 +24,6 @@ class PathautoGenerator implements PathautoGeneratorInterface { use StringTranslationTrait; - /** - * Punctuation characters cache. - * - * @var array - */ - protected $punctuationCharacters = array(); - /** * Config factory. * @@ -53,12 +46,19 @@ class PathautoGenerator implements PathautoGeneratorInterface { protected $token; /** - * Calculated patterns for entities. + * Calculated pattern for a specific entity. * * @var array */ protected $patterns = array(); + /** + * Available patterns per entity type ID. + * + * @var array + */ + protected $patternsByEntityType = array(); + /** * The alias cleaner. * @@ -129,7 +129,7 @@ public function createEntityAlias(EntityInterface $entity, $op) { return NULL; } - $source = '/' . $entity->urlInfo()->getInternalPath(); + $source = '/' . $entity->toUrl()->getInternalPath(); $config = $this->configFactory->get('pathauto.settings'); $langcode = $entity->language()->getId(); @@ -220,26 +220,40 @@ public function createEntityAlias(EntityInterface $entity, $op) { return $this->aliasStorageHelper->save($path, $existing_alias, $op); } + /** + * Loads pathauto patterns for a given entity type ID + * + * @param string $entity_type_id + * An entity type ID. + * + * @return \Drupal\pathauto\PathautoPatternInterface[] + * A list of patterns, sorted by weight. + */ + protected function getPatternByEntityType($entity_type_id) { + if (!isset($this->patternsByEntityType[$entity_type_id])) { + $ids = \Drupal::entityQuery('pathauto_pattern') + ->condition('type', array_keys(\Drupal::service('plugin.manager.alias_type') + ->getPluginDefinitionByType($entity_type_id))) + ->sort('weight') + ->execute(); + + $this->patternsByEntityType[$entity_type_id] = \Drupal::entityTypeManager() + ->getStorage('pathauto_pattern') + ->loadMultiple($ids); + } + + return $this->patternsByEntityType[$entity_type_id]; + } + /** * {@inheritdoc} */ public function getPatternByEntity(EntityInterface $entity) { if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()])) { - foreach (\Drupal::service('plugin.manager.alias_type')->getPluginDefinitionByType($entity->getEntityTypeId()) as $plugin_id => $plugin_definition) { - /** @var \Drupal\pathauto\PathautoPatternInterface[] $patterns */ - $patterns = \Drupal::entityManager() - ->getStorage('pathauto_pattern') - ->loadByProperties(['type' => $plugin_id]); - uasort($patterns, function (PathautoPatternInterface $a, PathautoPatternInterface $b) { - if ($a->getWeight() == $b->getWeight()) { - return 0; - } - return ($a->getWeight() > $b->getWeight()) ? -1 : 1; - }); - foreach ($patterns as $pattern) { - if ($pattern->applies($entity)) { - $this->patterns[$entity->getEntityTypeId()][$entity->id()] = $pattern; - } + foreach ($this->getPatternByEntityType($entity->getEntityTypeId()) as $pattern) { + if ($pattern->applies($entity)) { + $this->patterns[$entity->getEntityTypeId()][$entity->id()] = $pattern; + break; } } // If still not set. @@ -254,7 +268,8 @@ public function getPatternByEntity(EntityInterface $entity) { * {@inheritdoc} */ public function resetCaches() { - $this->patterns = array(); + $this->patterns = []; + $this->patternsByEntityType = []; $this->aliasCleaner->resetCaches(); } diff --git a/src/PathautoGeneratorInterface.php b/src/PathautoGeneratorInterface.php index 057692e..e6371b1 100644 --- a/src/PathautoGeneratorInterface.php +++ b/src/PathautoGeneratorInterface.php @@ -7,7 +7,6 @@ namespace Drupal\pathauto; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Language\LanguageInterface; /** * Provides and interface for PathautoGenerator. @@ -50,7 +49,7 @@ interface PathautoGeneratorInterface { public function resetCaches(); /** - * Load an URL alias pattern by entity, bundle, and language. + * Load an alias pattern entity by entity, bundle, and language. * * @param \Drupal\Core\Entity\EntityInterface $entity * An entity. From 58e144313898786c2ae57d21ba2825aee23a904b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 18 Nov 2015 00:45:55 +0100 Subject: [PATCH 121/169] Fixing more tests --- pathauto.module | 6 +++--- src/Tests/PathautoTaxonomyWebTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pathauto.module b/pathauto.module index ff47815..c5ca6d3 100644 --- a/pathauto.module +++ b/pathauto.module @@ -65,9 +65,9 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { */ function pathauto_entity_bundle_delete($entity_type, $bundle) { // @todo Update or remove this. - $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); - $config->clear('patterns.' . $entity_type . '.bundles.' . $bundle); - $config->save(); + // $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); + // $config->clear('patterns.' . $entity_type . '.bundles.' . $bundle); + // $config->save(); } diff --git a/src/Tests/PathautoTaxonomyWebTest.php b/src/Tests/PathautoTaxonomyWebTest.php index 12da07f..4183f27 100644 --- a/src/Tests/PathautoTaxonomyWebTest.php +++ b/src/Tests/PathautoTaxonomyWebTest.php @@ -47,7 +47,7 @@ function setUp() { $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); - $this->createPattern('taxonomy_term', '/[term:parent:url:path]/[term:name]'); + $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]'); } From 2e5b49eff5825729317a09e1b5594af18c208828 Mon Sep 17 00:00:00 2001 From: Vasile Chindris Date: Sat, 21 Nov 2015 20:49:28 +0100 Subject: [PATCH 122/169] Tests for the mass deletion of url aliases. --- src/Tests/PathautoMassDeleteTest.php | 169 +++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/Tests/PathautoMassDeleteTest.php diff --git a/src/Tests/PathautoMassDeleteTest.php b/src/Tests/PathautoMassDeleteTest.php new file mode 100644 index 0000000..318760c --- /dev/null +++ b/src/Tests/PathautoMassDeleteTest.php @@ -0,0 +1,169 @@ +adminUser = $this->drupalCreateUser($permissions); + $this->drupalLogin($this->adminUser); + } + + /** + * Tests the deletion of all the aliases. + */ + function testDeleteAll() { + // 1. Test that deleting all the aliases, of any type, works. + $this->generateAliases(); + $edit = array( + 'delete[all_aliases]' => TRUE, + ); + $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!')); + $this->assertText(t('All of your path aliases have been deleted.')); + + // Make sure that all of them are actually deleted. + $aliases = db_select('url_alias', 'ua')->fields('ua', array())->execute()->fetchAll(); + $this->assertEqual($aliases, array(), "All the aliases have been deleted."); + + // 2. Test deleting only specific (entity type) aliases. + $manager = $this->container->get('plugin.manager.alias_type'); + $pathauto_plugins = array('node' => 'nodes', 'taxonomy_term' => 'terms', 'user' => 'accounts'); + foreach ($pathauto_plugins as $pathauto_plugin => $attribute) { + $this->generateAliases(); + $edit = array( + 'delete[plugins][' . $pathauto_plugin . ']' => TRUE, + ); + $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!')); + $alias_type = $manager->createInstance($pathauto_plugin); + $this->assertRaw(t('All of your %label path aliases have been deleted.', array('%label' => $alias_type->getLabel()))); + // Check that the aliases were actually deleted. + foreach ($this->{$attribute} as $entity) { + $this->assertNoEntityAlias($entity); + } + + // Check that the other aliases are not deleted. + foreach ($pathauto_plugins as $_pathauto_plugin => $_attribute) { + // Skip the aliases that should be deleted. + if ($_pathauto_plugin == $pathauto_plugin) { + continue; + } + foreach ($this->{$_attribute} as $entity) { + $this->assertEntityAliasExists($entity); + } + } + } + } + + /** + * Helper function to generate aliases. + */ + function generateAliases() { + // We generate a bunch of aliases for nodes, users and taxonomy terms. If + // the entities are already created we just update them, otherwise we create + // them. + if (empty($this->nodes)) { + for ($i = 1; $i <= 5; $i++) { + $node = $this->drupalCreateNode(); + $this->nodes[$node->id()] = $node; + } + } + else { + foreach ($this->nodes as $node) { + $node->save(); + } + } + + if (empty($this->accounts)) { + for ($i = 1; $i <= 5; $i++) { + $account = $this->drupalCreateUser(); + $this->accounts[$account->id()] = $account; + } + } + else { + foreach ($this->accounts as $id => $account) { + $account->save(); + } + } + + if (empty($this->terms)) { + $vocabulary = $this->addVocabulary(array('name' => 'test vocabulary', 'vid' => 'test_vocabulary')); + for ($i = 1; $i <= 5; $i++) { + $term = $this->addTerm($vocabulary); + $this->terms[$term->id()] = $term; + } + } + else { + foreach ($this->terms as $term) { + $term->save(); + } + } + + // Check that we have aliases for the entities. + foreach (array('nodes', 'accounts', 'terms') as $attribute) { + foreach ($this->{$attribute} as $entity) { + $this->assertEntityAliasExists($node); + } + } + } + +} From c0be620c62e44cb708e993e45367cf72badeecb5 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 23 Nov 2015 15:15:26 -0600 Subject: [PATCH 123/169] updating method names and signatures to conform to newest ctools update. --- src/Form/CriteriaDelete.php | 2 +- src/Form/CriteriaForm.php | 2 +- src/Form/SelectionCriteriaForm.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Form/CriteriaDelete.php b/src/Form/CriteriaDelete.php index 260d94d..6463d55 100644 --- a/src/Form/CriteriaDelete.php +++ b/src/Form/CriteriaDelete.php @@ -16,7 +16,7 @@ class CriteriaDelete extends ConditionDelete { /** * {@inheritdoc} */ - protected function getRouteInfo() { + protected function getParentRouteInfo($cached_values) { return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'selection_criteria']]; } diff --git a/src/Form/CriteriaForm.php b/src/Form/CriteriaForm.php index f7f62cd..7d6575c 100644 --- a/src/Form/CriteriaForm.php +++ b/src/Form/CriteriaForm.php @@ -16,7 +16,7 @@ class CriteriaForm extends ConditionConfigure { /** * {@inheritdoc} */ - protected function getParentRouteInfo() { + protected function getParentRouteInfo($cached_values) { return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'selection_criteria']]; } diff --git a/src/Form/SelectionCriteriaForm.php b/src/Form/SelectionCriteriaForm.php index 4dad025..a751831 100644 --- a/src/Form/SelectionCriteriaForm.php +++ b/src/Form/SelectionCriteriaForm.php @@ -30,7 +30,7 @@ protected function getConditionClass() { /** * {@inheritdoc} */ - protected function getAddRoute() { + protected function getAddRoute($cached_values) { return 'pathauto.pattern.condition.add'; } @@ -44,7 +44,7 @@ protected function getTempstoreId() { /** * {@inheritdoc} */ - protected function getOperationsRouteInfo($machine_name, $row) { + protected function getOperationsRouteInfo($cached_values, $machine_name, $row) { return ['pathauto.pattern.condition', ['machine_name' => $machine_name, 'condition' => $row]]; } From 54248b821095d8787512e5f1b44c84fa8dfba1f3 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 24 Nov 2015 00:55:01 +0100 Subject: [PATCH 124/169] Fix NodeAliasTest --- src/Tests/AliasType/NodeAliasTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Tests/AliasType/NodeAliasTest.php b/src/Tests/AliasType/NodeAliasTest.php index c5bfede..c7c7388 100644 --- a/src/Tests/AliasType/NodeAliasTest.php +++ b/src/Tests/AliasType/NodeAliasTest.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto\Tests\AliasType; +use Drupal\node\Entity\NodeType; use Drupal\simpletest\KernelTestBase; /** @@ -30,12 +31,14 @@ public function testNodeAlias() { /** @var \Drupal\pathauto\AliasTypeManager $manager */ $manager = $this->container->get('plugin.manager.alias_type'); + NodeType::create(['type' => 'article', 'name' => 'Article'])->save(); + /** @var \Drupal\pathauto\AliasTypeInterface $node_type */ $node_type = $manager->createInstance('node'); $patterns = $node_type->getPatterns(); - $this->assertTrue((array_key_exists('node', $patterns)), "Node pattern exists."); - $this->assertEqual($patterns['node'], 'Pattern for all Content paths', "Node pattern description matches."); + $this->assertTrue((array_key_exists('article', $patterns)), "Article pattern exists."); + $this->assertEqual($patterns['article'], 'Pattern for all Article paths', "Article pattern description matches."); $token_types = $node_type->getTokenTypes(); $this->assertTrue(in_array('node', $token_types), "Node token type exists."); From b1fd07f70e9224dbb4690af40652a60106f9b9f9 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 24 Nov 2015 15:12:29 -0600 Subject: [PATCH 125/169] Abstracting the relationship forms into ctools and rebuilding pathauto implementation as subclasses --- src/Form/AddContext.php | 164 +++------------------------------- src/Form/ContextConfigure.php | 130 ++------------------------- src/Form/ContextDelete.php | 102 +++------------------ 3 files changed, 32 insertions(+), 364 deletions(-) diff --git a/src/Form/AddContext.php b/src/Form/AddContext.php index a1d6e49..9f6c048 100644 --- a/src/Form/AddContext.php +++ b/src/Form/AddContext.php @@ -6,20 +6,9 @@ namespace Drupal\pathauto\Form; -use Drupal\Component\Serialization\Json; -use Drupal\Core\Ajax\AjaxResponse; -use Drupal\Core\Ajax\OpenModalDialogCommand; -use Drupal\Core\Form\FormBase; -use Drupal\Core\Form\FormBuilderInterface; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; +use Drupal\ctools\Form\ManageContext; -class AddContext extends FormBase { - - /** - * @var string - */ - protected $machine_name; +class AddContext extends ManageContext { /** * An array of property types that are eligible as relationships. @@ -32,164 +21,32 @@ class AddContext extends FormBase { * {@inheritdoc} */ public function getFormId() { - return 'ctools_add_context_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $cached_values = $form_state->getTemporaryValue('wizard'); - $this->machine_name = $cached_values['id']; - $form['items'] = array( - '#type' => 'markup', - '#prefix' => '
', - '#suffix' => '
', - '#theme' => 'table', - '#header' => array($this->t('Context ID'), $this->t('Label'), $this->t('Data Type'), $this->t('Options')), - '#rows' => $this->renderRows($cached_values), - '#empty' => t('No contexts or relationships have been added.') - ); - - $form['relationships'] = [ - '#type' => 'select', - '#title' => $this->t('Add a relationship'), - '#options' => $this->getAvailableRelationships($cached_values), - ]; - $form['add_relationship'] = [ - '#type' => 'submit', - '#name' => 'add', - '#value' => t('Add Relationship'), - '#ajax' => [ - 'callback' => [$this, 'addRelationship'], - 'event' => 'click', - ], - '#submit' => [ - 'callback' => [$this, 'submitForm'], - ] - ]; - return $form; + return 'pathauto_add_context_form'; } /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state) { - if ($form_state->getTriggeringElement()['#name'] == 'add') { - list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $form_state->getValue('relationships')); - $form_state->setRedirect($this->getAddRoute(), $route_parameters); - } - } - - public function addRelationship(array &$form, FormStateInterface $form_state) { - $relationship = $form_state->getValue('relationships'); - $content = \Drupal::formBuilder()->getForm($this->getContextClass(), $relationship, $this->getTempstoreId(), $this->machine_name); - $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; - list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $relationship); - $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getAddRoute(), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); - $response = new AjaxResponse(); - $response->addCommand(new OpenModalDialogCommand($this->t('Configure Relationship'), $content, array('width' => '700'))); - return $response; - } - - protected function getAvailableRelationships($cached_values) { - /** @var \Drupal\ctools\TypedDataResolver $resolver */ - $resolver = \Drupal::service('ctools.typed_data.resolver'); - return $resolver->getTokensOfDataType($this->getContexts($cached_values), $this->property_types); - } - - /** - * @param $cached_values - * - * @return array - */ - protected function renderRows($cached_values) { - $contexts = array(); - foreach ($this->getContexts($cached_values) as $row => $context) { - list($route_name, $route_parameters) = $this->getRelationshipOperationsRouteInfo($this->machine_name, $row); - $build = array( - '#type' => 'operations', - '#links' => $this->getOperations($row, $route_name, $route_parameters), - ); - $contexts[$row] = array( - $row, - $context->getContextDefinition()->getLabel(), - $context->getContextDefinition()->getDataType(), - 'operations' => [ - 'data' => $build, - ], - ); - } - return $contexts; - } - - protected function getOperations($row, $route_name_base, array $route_parameters = array()) { - // Base contexts will not be a : separated and generated relationships should have 3 parts. - if (count(explode(':', $row)) < 2) { - return []; - } - $operations['edit'] = array( - 'title' => t('Edit'), - 'url' => new Url($route_name_base . '.edit', $route_parameters), - 'weight' => 10, - 'attributes' => array( - 'class' => ['use-ajax'], - 'data-dialog-type' => 'modal', - 'data-dialog-options' => Json::encode([ - 'width' => 700, - ]), - ), - ); - $route_parameters['id'] = $route_parameters['context']; - $operations['delete'] = array( - 'title' => t('Delete'), - 'url' => new Url($route_name_base . '.delete', $route_parameters), - 'weight' => 100, - 'attributes' => array( - 'class' => array('use-ajax'), - 'data-dialog-type' => 'modal', - 'data-dialog-options' => Json::encode([ - 'width' => 700, - ]), - ), - ); - return $operations; - } - - /** - * Return a subclass of '\Drupal\ctools\Form\ContextConfigure'. - * - * The ConditionConfigure class is designed to be subclassed with custom - * route information to control the modal/redirect needs of your use case. - * - * @return string - */ - protected function getContextClass() { + protected function getContextClass($cached_values) { return ContextConfigure::class; } /** - * The route to which condition 'add' actions should submit. - * - * @return string + * {@inheritdoc} */ - protected function getAddRoute() { + protected function getAddRoute($cached_values) { return 'pathauto.pattern.relationship.add'; } /** - * Provide the tempstore id for your specified use case. - * - * @return string + * {@inheritdoc} */ protected function getTempstoreId() { return 'pathauto.pattern'; } /** - * @param $cached_values - * - * @return \Drupal\Core\Plugin\Context\ContextInterface[] + * {@inheritdoc} */ protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ @@ -197,7 +54,10 @@ protected function getContexts($cached_values) { return $pattern->getContexts(); } - protected function getRelationshipOperationsRouteInfo($machine_name, $row) { + /** + * {@inheritdoc} + */ + protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row) { return ['pathauto.pattern.relationship', ['machine_name' => $machine_name, 'context' => $row]]; } diff --git a/src/Form/ContextConfigure.php b/src/Form/ContextConfigure.php index 2344ef9..2001999 100644 --- a/src/Form/ContextConfigure.php +++ b/src/Form/ContextConfigure.php @@ -6,96 +6,18 @@ namespace Drupal\pathauto\Form; -use Drupal\Core\Ajax\AjaxResponse; -use Drupal\Core\Ajax\CloseModalDialogCommand; -use Drupal\Core\Ajax\RedirectCommand; -use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\Context\Context; use Drupal\Core\Plugin\Context\ContextDefinition; -use Drupal\ctools\TypedDataResolver; -use Drupal\user\SharedTempStoreFactory; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\ctools\Form\RelationshipConfigure; -class ContextConfigure extends FormBase { - - /** - * @var \Drupal\user\SharedTempStoreFactory - */ - protected $tempstore; - - /** - * @var \Drupal\ctools\TypedDataResolver - */ - protected $resolver; - - /** - * @var string - */ - protected $tempstore_id; - - /** - * @var string; - */ - protected $machine_name; - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static($container->get('user.shared_tempstore'), $container->get('ctools.typed_data.resolver')); - } - - public function __construct(SharedTempStoreFactory $tempstore, TypedDataResolver $resolver) { - $this->tempstore = $tempstore; - $this->resolver = $resolver; - } +class ContextConfigure extends RelationshipConfigure { /** * {@inheritdoc} */ public function getFormId() { - return 'ctools_context_configure'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, $context = NULL, $tempstore_id = NULL, $machine_name = NULL) { - $this->tempstore_id = $tempstore_id; - $this->machine_name = $machine_name; - $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name); - - /** @var \Drupal\Core\Plugin\Context\ContextInterface[] $contexts */ - $contexts = $this->getContexts($cached_values); - $context_object = $this->resolver->convertTokenToContext($context, $contexts); - $form['id'] = [ - '#type' => 'value', - '#value' => $context - ]; - $form['context_object'] = [ - '#type' => 'value', - '#value' => $context_object, - ]; - $form['context_data'] = [ - '#type' => 'item', - '#title' => $this->resolver->getLabelByToken($context, $contexts), - '#markup' => $context_object->getContextDefinition()->getDataType(), - ]; - $form['label'] = [ - '#type' => 'textfield', - '#title' => $this->t('Context label'), - '#default_value' => !empty($contexts[$context]) ? $contexts[$context]->getContextDefinition()->getLabel() : '', - '#required' => TRUE, - ]; - $form['submit'] = [ - '#type' => 'submit', - '#value' => $this->t('Save'), - '#ajax' => [ - 'callback' => [$this, 'ajaxSave'], - ] - ]; - return $form; + return 'pathauto_relationship_configure'; } /** @@ -121,59 +43,23 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $pattern->replaceContext($form_state->getValue('id'), $new_context); } $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); - list($route_name, $route_options) = $this->getParentRouteInfo(); - $form_state->setRedirect($route_name, $route_options); - } - - public function ajaxSave(array &$form, FormStateInterface $form_state) { - $response = new AjaxResponse(); - list($route_name, $route_parameters) = $this->getParentRouteInfo(); - $response->addCommand(new RedirectCommand($this->url($route_name, $route_parameters))); - $response->addCommand(new CloseModalDialogCommand()); - return $response; + parent::submitForm($form, $form_state); } /** - * Document the route name and parameters for redirect after submission. - * - * @return array - * In the format of - * return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name']]; + * {@inheritdoc} */ - protected function getParentRouteInfo() { + protected function getParentRouteInfo($cached_values) { return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'contexts']]; } /** - * @param $context - * - * @return array - * In the format of - * return ['route.name', ['machine_name' => $this->machine_name, 'context' => $context]]; - */ - protected function getRouteInfo($context) { - return ['pathauto.pattern.relationship.add', ['machine_name' => $this->machine_name, 'context' => $context]]; - } - - /** - * Custom logic for setting the conditions array in cached_values. - * - * @param $cached_values - * - * @param $contexts - * The conditions to set within the cached values. - * - * @return mixed - * Return the $cached_values + * {@inheritdoc} */ protected function setContexts($cached_values, $contexts) {} /** - * Custom logic for retrieving the contexts array from cached_values. - * - * @param $cached_values - * - * @return \Drupal\Core\Plugin\Context\ContextInterface[] + * {@inheritdoc} */ protected function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ diff --git a/src/Form/ContextDelete.php b/src/Form/ContextDelete.php index 8a4830a..58c82ec 100644 --- a/src/Form/ContextDelete.php +++ b/src/Form/ContextDelete.php @@ -7,118 +7,40 @@ namespace Drupal\pathauto\Form; -use Drupal\Core\Form\ConfirmFormBase; -use Drupal\Core\Form\ConfirmFormHelper; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\ctools\TypedDataResolver; -use Drupal\user\SharedTempStoreFactory; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\ctools\Form\RelationshipDelete; -class ContextDelete extends ConfirmFormBase { - - /** - * @var \Drupal\user\SharedTempStoreFactory - */ - protected $tempstore; - - /** - * @var \Drupal\ctools\TypedDataResolver - */ - protected $resolver; - - /** - * @var string - */ - protected $tempstore_id; - - /** - * @var string; - */ - protected $machine_name; - - /** - * @var string; - */ - protected $id; +class ContextDelete extends RelationshipDelete { /** * {@inheritdoc} */ - public static function create(ContainerInterface $container) { - return new static($container->get('user.shared_tempstore'), $container->get('ctools.typed_data.resolver')); - } - - public function __construct(SharedTempStoreFactory $tempstore, TypedDataResolver $resolver) { - $this->tempstore = $tempstore; - $this->resolver = $resolver; - } - public function getFormId() { - return 'ctools_context_delete'; - } - - public function getQuestion($id = NULL, $cached_values = NULL) { - $context = $this->getContexts($cached_values)[$id]; - return $this->t('Are you sure you want to delete the @label context?', [ - '@label' => $context->getContextDefinition()->getLabel(), - ]); + return 'pathauto_relationship_delete'; } - public function getCancelUrl() { + /** + * {@inheritdoc} + */ + public function getCancelUrl($cached_values = []) { return new Url('entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'contexts']); } - public function buildForm(array $form, FormStateInterface $form_state, $id = NULL, $tempstore_id = NULL, $machine_name = NULL) { - $this->tempstore_id = $tempstore_id; - $this->machine_name = $machine_name; - $this->id = $id; - - $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name); - $form ['#title'] = $this->getQuestion($id, $cached_values); - - $form ['#attributes']['class'][] = 'confirmation'; - $form ['description'] = array('#markup' => $this->getDescription()); - $form [$this->getFormName()] = array('#type' => 'hidden', '#value' => 1); - - // By default, render the form using theme_confirm_form(). - if (!isset($form ['#theme'])) { - $form ['#theme'] = 'confirm_form'; - } - $form['actions'] = array('#type' => 'actions'); - $form['actions'] += $this->actions($form, $form_state); - return $form; - } - + /** + * {@inheritdoc} + */ public function submitForm(array &$form, FormStateInterface $form_state) { $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);; /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ $pattern = $cached_values['pathauto_pattern']; $pattern->removeContext($this->id); $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); - $form_state->setRedirectUrl($this->getCancelUrl()); - } - - protected function actions(array $form, FormStateInterface $form_state) { - return array( - 'submit' => array( - '#type' => 'submit', - '#value' => $this->getConfirmText(), - '#validate' => array( - array($this, 'validate'), - ), - '#submit' => array( - array($this, 'submitForm'), - ), - ), - 'cancel' => ConfirmFormHelper::buildCancelLink($this, $this->getRequest()), - ); + parent::submitForm($form, $form_state); } /** - * @param $cached_values - * - * @return \Drupal\Core\Plugin\Context\ContextInterface[] + * {@inheritdoc} */ public function getContexts($cached_values) { /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ From 1da2e203945457bb5c520c9a29fe8b6f93e9717c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 26 Nov 2015 08:12:42 +0100 Subject: [PATCH 126/169] Added update function for widget change --- pathauto.install | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pathauto.install b/pathauto.install index ffc1490..e7dc77d 100644 --- a/pathauto.install +++ b/pathauto.install @@ -7,6 +7,8 @@ * @ingroup pathauto */ +use Drupal\Core\Entity\Entity\EntityFormDisplay; + /** * Implements hook_install(). */ @@ -15,3 +17,18 @@ function pathauto_install() { module_set_weight('pathauto', 1); } + +/** + * Updates pathauto widgets to use the path widget ID. + */ +function pathauto_update_8002() { + foreach (EntityFormDisplay::loadMultiple() as $form_display) { + if ($component = $form_display->getComponent('path')) { + if (isset($component['type']) && $component['type'] == 'pathauto') { + $component['type'] = 'path'; + $form_display->setComponent('path', $component); + $form_display->save(); + } + } + } +} From 69add3dadf06df3dacc99772c852e9aa6025557d Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 26 Nov 2015 08:17:51 +0100 Subject: [PATCH 127/169] Also added code to update installed entity definitions --- pathauto.install | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pathauto.install b/pathauto.install index e7dc77d..ee5a667 100644 --- a/pathauto.install +++ b/pathauto.install @@ -21,7 +21,26 @@ function pathauto_install() { /** * Updates pathauto widgets to use the path widget ID. */ -function pathauto_update_8002() { +function pathauto_update_8001() { + + // Replace values in the 'entity.definitions.installed' keyvalue collection. + $collection = \Drupal::service('keyvalue')->get('entity.definitions.installed'); + foreach ($collection->getAll() as $key => $definitions) { + if (!is_array($definitions) || empty($definitions['path'])) { + continue; + } + + // Retrieve and change path base field definition. + $path_definition = $definitions['path']; + if (($options = $path_definition->getDisplayOptions('form')) && $options['type'] = 'pathauto') { + $options['type'] = 'path'; + $path_definition->setDisplayOptions('form', $options); + // Save the new value. + $collection->set($key, $path_definition); + } + + } + foreach (EntityFormDisplay::loadMultiple() as $form_display) { if ($component = $form_display->getComponent('path')) { if (isset($component['type']) && $component['type'] == 'pathauto') { From 3f68a032d48b3d5bafbb6e277a9e4af63729ca0a Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Mon, 19 Oct 2015 22:51:28 +0200 Subject: [PATCH 128/169] =?UTF-8?q?Issue=20#936222=20by=20mvc,=20quicksket?= =?UTF-8?q?ch,=20joelpittet,=20Dave=20Reid,=20sammarks15,=20brianV,=20Rolo?= =?UTF-8?q?DMonkey,=20bvanmeurs,=20effulgentsia,=20jucallme,=20ponies,=20I?= =?UTF-8?q?sland=20Usurper,=20Pancho,=20iamEAP,=20pinoniq,=20bibishani,=20?= =?UTF-8?q?G=C3=A1bor=20Hojtsy,=20emanaton,=20populist,=20mrmikedewolf,=20?= =?UTF-8?q?klonos,=20dsnopek,=20Fabianx,=20MiroslavBanov,=20JamesOakley,?= =?UTF-8?q?=20acbramley,=20jstoller,=20DamienMcKenna,=20mattlt,=20rv0:=20M?= =?UTF-8?q?erged=20in=20pathauto=5Fpersist=20module=20functionality=20to?= =?UTF-8?q?=20prevent=20losing=20manual=20aliases=20when=20calling=20node?= =?UTF-8?q?=5Fsave().?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pathauto.install | 75 ++++++++++++++++-- pathauto.module | 108 ++++++++++++++++++++++++++ src/PathautoManager.php | 2 + src/Tests/PathautoBulkUpdateTest.php | 6 +- src/Tests/PathautoNodeWebTest.php | 81 +++++++++++++++++++ src/Tests/PathautoTestHelperTrait.php | 8 +- src/Tests/PathautoUnitTest.php | 1 + 7 files changed, 271 insertions(+), 10 deletions(-) diff --git a/pathauto.install b/pathauto.install index ee5a667..1c6f111 100644 --- a/pathauto.install +++ b/pathauto.install @@ -6,6 +6,40 @@ * * @ingroup pathauto */ +use Drupal\Core\Database\Database; + +/** + * Implements hook_schema(). + */ +function pathauto_schema() { + $schema['pathauto_state'] = array( + 'description' => 'The status of each entity alias (whether it was automatically generated or not).', + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => 'An entity type.', + ), + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'An entity ID.', + ), + 'pathauto' => array( + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The automatic alias status of the entity.', + ), + ), + 'primary key' => array('entity_type', 'entity_id'), + ); + + return $schema; +} use Drupal\Core\Entity\Entity\EntityFormDisplay; @@ -22,14 +56,13 @@ function pathauto_install() { * Updates pathauto widgets to use the path widget ID. */ function pathauto_update_8001() { - // Replace values in the 'entity.definitions.installed' keyvalue collection. - $collection = \Drupal::service('keyvalue')->get('entity.definitions.installed'); + $collection = \Drupal::service('keyvalue') + ->get('entity.definitions.installed'); foreach ($collection->getAll() as $key => $definitions) { if (!is_array($definitions) || empty($definitions['path'])) { continue; } - // Retrieve and change path base field definition. $path_definition = $definitions['path']; if (($options = $path_definition->getDisplayOptions('form')) && $options['type'] = 'pathauto') { @@ -38,9 +71,7 @@ function pathauto_update_8001() { // Save the new value. $collection->set($key, $path_definition); } - } - foreach (EntityFormDisplay::loadMultiple() as $form_display) { if ($component = $form_display->getComponent('path')) { if (isset($component['type']) && $component['type'] == 'pathauto') { @@ -51,3 +82,37 @@ function pathauto_update_8001() { } } } + +/** + * Create pathauto_state table, using data from pathauto_persist if it exists. + */ +function pathauto_update_8002() { + if (!Database::getConnection()->schema()->tableExists('pathauto_state')) { + + $schema['pathauto_state'] = array( + 'description' => 'The status of each entity alias (whether it was automatically generated or not).', + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => 'The entity type.', + ), + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity ID.', + ), + 'pathauto' => array( + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The automatic alias status of the entity.', + ), + ), + 'primary key' => array('entity_type', 'entity_id'), + ); + } +} diff --git a/pathauto.module b/pathauto.module index 5faa79e..98c021e 100644 --- a/pathauto.module +++ b/pathauto.module @@ -69,6 +69,33 @@ function pathauto_entity_bundle_delete($entity_type, $bundle) { $config->save(); } +/** + * Implements hook_entity_load(). + */ +function pathauto_entity_load($entities, $entity_type) { + // Statically cache which entity types have data in the pathauto_state + // table to avoid unnecessary queries for entities that would not have any + // data anyway. + static $loadable_types; + if (!isset($loadable_types)) { + $loadable_types = &drupal_static(__FUNCTION__); + if (!isset($loadable_types)) { + $loadable_types = db_query("SELECT DISTINCT entity_type FROM {pathauto_state}")->fetchCol(); + } + } + + // Check if this entity type has loadable records. + if (!in_array($entity_type, $loadable_types)) { + return; + } + + $states = pathauto_entity_state_load_multiple($entity_type, array_keys($entities)); + foreach ($states as $id => $state) { + if ($entities[$id]->hasField('path') && !isset($entities[$id]->path->pathauto)) { + $entities[$id]->path->pathauto = $state; + } + } +} /** * Implements hook_entity_presave(). @@ -77,6 +104,13 @@ function pathauto_entity_presave($entity) { if (!($entity instanceof ContentEntityInterface) || $entity->hasField('path')) { return; } + + if (isset($entity->path->pathauto)) { + // We must set an empty alias string for the path to prevent saving an + // alias. + $entity->path->alias = ''; + } + // About to be saved (before insert/update) if (!empty($entity->path->pathauto) && isset($entity->path->old_alias) && $entity->path->alias == '' && $entity->path->old_alias != '') { @@ -120,6 +154,80 @@ function pathauto_entity_delete(EntityInterface $entity) { if ($entity->hasLinkTemplate('canonical')) { \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity); } + if ($entity instanceof ContentEntityInterface && $entity->hasField('path') && isset($entity->path->pathauto)) { + pathauto_entity_state_delete($entity); + } +} + + +/** + * Load a pathauto state for an entity. + * + * @param string $entity_type + * An entity type. + * @param int $entity_id + * An entity ID. + * + * @return bool + * A value that evaluates to TRUE if Pathauto should control this entity's + * path. A value that evaluates to FALSE if Pathauto should not manage the + * entity's path. + */ +function pathauto_entity_state_load($entity_type, $entity_id) { + $pathauto_state = pathauto_entity_state_load_multiple($entity_type, array($entity_id)); + return !empty($pathauto_state) ? reset($pathauto_state) : FALSE; +} + +/** + * Load a pathauto state for multiple entities. + * + * @param string $entity_type + * The entity type. + * @param int[] $entity_ids + * The array of entity IDs. + * + * @return bool[] + * An array of Pathauto states keyed by entity ID. + */ +function pathauto_entity_state_load_multiple($entity_type, $entity_ids) { + return db_query("SELECT entity_id, pathauto FROM {pathauto_state} WHERE entity_type = :entity_type AND entity_id IN (:entity_ids[])", array(':entity_type' => $entity_type, ':entity_ids[]' => $entity_ids))->fetchAllKeyed(); +} + +/** + * Save the pathauto state for an entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity object. + * @param bool $pathauto_state + * A value that evaluates to TRUE means that Pathauto should keep controlling + * this entity's path in the future. A value that evaluates to FALSE means + * that Pathauto should not manage the entity's path. + */ +function pathauto_entity_state_save($entity, $pathauto_state) { + db_merge('pathauto_state') + ->key(array( + 'entity_type' => $entity->getEntityTypeId(), + 'entity_id' => $entity->id(), + )) + ->fields(array( + 'pathauto' => $pathauto_state ? 1 : 0, + )) + ->execute(); + drupal_static_reset('pathauto_entity_load'); +} + +/** + * Delete the pathauto state for an entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity object. + */ +function pathauto_entity_state_delete($entity) { + db_delete('pathauto_state') + ->condition('entity_type', $entity->getEntityTypeId()) + ->condition('entity_id', $entity->id()) + ->execute(); + drupal_static_reset('pathauto_entity_load'); } /** diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 9d5abf5..aa8a6dd 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -267,6 +267,8 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array return NULL; } + pathauto_entity_state_save($entity, $entity->path->pathauto); + $options += array('language' => $entity->language()->getId()); $type = $entity->getEntityTypeId(); $bundle = $entity->bundle(); diff --git a/src/Tests/PathautoBulkUpdateTest.php b/src/Tests/PathautoBulkUpdateTest.php index f9f03ec..8cc8e90 100644 --- a/src/Tests/PathautoBulkUpdateTest.php +++ b/src/Tests/PathautoBulkUpdateTest.php @@ -84,10 +84,10 @@ function testBulkUpdate() { // Add a new node. $new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => FALSE))); - // Run the update again which should only run against the new node. + // Run the update again which should not run against any nodes. $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); - $this->assertText('Generated 1 URL alias.'); // 1 node + 0 users + $this->assertText('No new URL aliases to generate.'); - $this->assertEntityAliasExists($new_node); + $this->assertNoEntityAliasExists($new_node); } } diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index eb68e9e..d9884ce 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\pathauto\Tests; +use Drupal\node\Entity\Node; use Drupal\simpletest\WebTestBase; /** @@ -162,4 +163,84 @@ function testNodeOperations() { $this->assertEntityAlias($node2, '/node/' . $node2->id()); } + /** + * @todo Merge this with existing node test methods? + */ + public function testNodeState() { + $nodeNoAliasUser = $this->drupalCreateUser(array('bypass node access')); + $nodeAliasUser = $this->drupalCreateUser(array('bypass node access', 'create url aliases')); + + $node = $this->drupalCreateNode(array( + 'title' => 'Node version one', + 'type' => 'page', + 'path' => array( + 'pathauto' => FALSE, + ), + )); + + $this->assertNoEntityAlias($node); + + // Set a manual path alias for the node. + $node->path->alias = '/test-alias'; + $node->save(); + + // Ensure that the pathauto field was saved to the database. + \Drupal::entityManager()->getStorage('node')->resetCache(); + $node = Node::load($node->id()); + $this->assertFalse($node->path->pathauto); + + // Ensure that the manual path alias was saved and an automatic alias was not generated. + $this->assertEntityAlias($node, '/test-alias'); + $this->assertNoEntityAliasExists($node, '/content/node-version-one'); + + // Save the node as a user who does not have access to path fieldset. + $this->drupalLogin($nodeNoAliasUser); + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertNoFieldByName('path[0][pathauto]'); + + $edit = array('title[0][value]' => 'Node version two'); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Basic page Node version two has been updated.'); + + $this->assertEntityAlias($node, '/test-alias'); + $this->assertNoEntityAliasExists($node, '/content/node-version-one'); + $this->assertNoEntityAliasExists($node, '/content/node-version-two'); + + // Load the edit node page and check that the Pathauto checkbox is unchecked. + $this->drupalLogin($nodeAliasUser); + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertNoFieldChecked('edit-path-0-pathauto'); + + // Edit the manual alias and save the node. + $edit = array( + 'title[0][value]' => 'Node version three', + 'path[0][alias]' => '/manually-edited-alias', + ); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Basic page Node version three has been updated.'); + + $this->assertEntityAlias($node, '/manually-edited-alias'); + $this->assertNoEntityAliasExists($node, '/test-alias'); + $this->assertNoEntityAliasExists($node, '/content/node-version-one'); + $this->assertNoEntityAliasExists($node, '/content/node-version-two'); + $this->assertNoEntityAliasExists($node, '/content/node-version-three'); + + // Programatically save the node with an automatic alias. + \Drupal::entityManager()->getStorage('node')->resetCache(); + $node = Node::load($node->id()); + $node->path->pathauto = TRUE; + $node->save(); + + // Ensure that the pathauto field was saved to the database. + \Drupal::entityManager()->getStorage('node')->resetCache(); + $node = Node::load($node->id()); + $this->assertTrue($node->path->pathauto); + + $this->assertEntityAlias($node, '/content/node-version-three'); + $this->assertNoEntityAliasExists($node, '/manually-edited-alias'); + $this->assertNoEntityAliasExists($node, '/test-alias'); + $this->assertNoEntityAliasExists($node, '/content/node-version-one'); + $this->assertNoEntityAliasExists($node, '/content/node-version-two'); + } + } diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 4dec728..c201864 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -58,8 +58,12 @@ public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) { $this->assertEntityAlias($entity, '/' . $entity->urlInfo()->getInternalPath(), $langcode); } - public function assertNoEntityAliasExists(EntityInterface $entity) { - $this->assertNoAliasExists(array('source' => '/' . $entity->urlInfo()->getInternalPath())); + public function assertNoEntityAliasExists(EntityInterface $entity, $alias = NULL) { + $path = array('source' => '/' . $entity->urlInfo()->getInternalPath()); + if (!empty($alias)) { + $path['alias'] = $alias; + } + $this->assertNoAliasExists($path); } public function assertAlias($source, $expected_alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 4925ae7..bb65bfc 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -30,6 +30,7 @@ public function setUp() { parent::setup(); $this->installConfig(array('pathauto', 'taxonomy', 'system', 'node')); + $this->installSchema('pathauto', array('pathauto_state')); $this->installEntitySchema('user'); $this->installEntitySchema('node'); From fa45a59f802b3ee185dd48e5d783bc79e2d2b43d Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 29 Nov 2015 12:11:57 +0100 Subject: [PATCH 129/169] Add computed pathauto state field, store in key value --- pathauto.install | 75 +-------------- pathauto.module | 133 ++++---------------------- src/PathautoItem.php | 43 +++++++-- src/PathautoManager.php | 4 +- src/PathautoState.php | 121 +++++++++++++++++++++++ src/PathautoWidget.php | 18 +--- src/Plugin/Action/UpdateAction.php | 4 +- src/Tests/PathautoBulkUpdateTest.php | 9 +- src/Tests/PathautoLocaleTest.php | 3 +- src/Tests/PathautoMassDeleteTest.php | 2 +- src/Tests/PathautoNodeWebTest.php | 12 ++- src/Tests/PathautoTestHelperTrait.php | 4 +- src/Tests/PathautoUnitTest.php | 4 +- 13 files changed, 204 insertions(+), 228 deletions(-) create mode 100644 src/PathautoState.php diff --git a/pathauto.install b/pathauto.install index 1c6f111..ee5a667 100644 --- a/pathauto.install +++ b/pathauto.install @@ -6,40 +6,6 @@ * * @ingroup pathauto */ -use Drupal\Core\Database\Database; - -/** - * Implements hook_schema(). - */ -function pathauto_schema() { - $schema['pathauto_state'] = array( - 'description' => 'The status of each entity alias (whether it was automatically generated or not).', - 'fields' => array( - 'entity_type' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'description' => 'An entity type.', - ), - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'An entity ID.', - ), - 'pathauto' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The automatic alias status of the entity.', - ), - ), - 'primary key' => array('entity_type', 'entity_id'), - ); - - return $schema; -} use Drupal\Core\Entity\Entity\EntityFormDisplay; @@ -56,13 +22,14 @@ function pathauto_install() { * Updates pathauto widgets to use the path widget ID. */ function pathauto_update_8001() { + // Replace values in the 'entity.definitions.installed' keyvalue collection. - $collection = \Drupal::service('keyvalue') - ->get('entity.definitions.installed'); + $collection = \Drupal::service('keyvalue')->get('entity.definitions.installed'); foreach ($collection->getAll() as $key => $definitions) { if (!is_array($definitions) || empty($definitions['path'])) { continue; } + // Retrieve and change path base field definition. $path_definition = $definitions['path']; if (($options = $path_definition->getDisplayOptions('form')) && $options['type'] = 'pathauto') { @@ -71,7 +38,9 @@ function pathauto_update_8001() { // Save the new value. $collection->set($key, $path_definition); } + } + foreach (EntityFormDisplay::loadMultiple() as $form_display) { if ($component = $form_display->getComponent('path')) { if (isset($component['type']) && $component['type'] == 'pathauto') { @@ -82,37 +51,3 @@ function pathauto_update_8001() { } } } - -/** - * Create pathauto_state table, using data from pathauto_persist if it exists. - */ -function pathauto_update_8002() { - if (!Database::getConnection()->schema()->tableExists('pathauto_state')) { - - $schema['pathauto_state'] = array( - 'description' => 'The status of each entity alias (whether it was automatically generated or not).', - 'fields' => array( - 'entity_type' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'description' => 'The entity type.', - ), - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The entity ID.', - ), - 'pathauto' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The automatic alias status of the entity.', - ), - ), - 'primary key' => array('entity_type', 'entity_id'), - ); - } -} diff --git a/pathauto.module b/pathauto.module index 98c021e..17ac516 100644 --- a/pathauto.module +++ b/pathauto.module @@ -21,6 +21,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\pathauto\PathautoState; /** * The default ignore word list. @@ -69,34 +70,6 @@ function pathauto_entity_bundle_delete($entity_type, $bundle) { $config->save(); } -/** - * Implements hook_entity_load(). - */ -function pathauto_entity_load($entities, $entity_type) { - // Statically cache which entity types have data in the pathauto_state - // table to avoid unnecessary queries for entities that would not have any - // data anyway. - static $loadable_types; - if (!isset($loadable_types)) { - $loadable_types = &drupal_static(__FUNCTION__); - if (!isset($loadable_types)) { - $loadable_types = db_query("SELECT DISTINCT entity_type FROM {pathauto_state}")->fetchCol(); - } - } - - // Check if this entity type has loadable records. - if (!in_array($entity_type, $loadable_types)) { - return; - } - - $states = pathauto_entity_state_load_multiple($entity_type, array_keys($entities)); - foreach ($states as $id => $state) { - if ($entities[$id]->hasField('path') && !isset($entities[$id]->path->pathauto)) { - $entities[$id]->path->pathauto = $state; - } - } -} - /** * Implements hook_entity_presave(). */ @@ -105,15 +78,8 @@ function pathauto_entity_presave($entity) { return; } - if (isset($entity->path->pathauto)) { - // We must set an empty alias string for the path to prevent saving an - // alias. - $entity->path->alias = ''; - } - // About to be saved (before insert/update) - if (!empty($entity->path->pathauto) && isset($entity->path->old_alias) - && $entity->path->alias == '' && $entity->path->old_alias != '') { + if ($entity->path->pathauto == PathautoState::SKIP && $entity->path->old_alias != '') { /* * There was an old alias, but when pathauto_perform_alias was checked * the javascript disabled the textbox which led to an empty value being @@ -122,14 +88,6 @@ function pathauto_entity_presave($entity) { */ $entity->path->alias = $entity->path->old_alias; } - - // Help prevent errors with progromatically creating entities by defining - // path['alias'] as an empty string. - // @see http://drupal.org/node/1328180 - // @see http://drupal.org/node/1576552 - if (isset($entity->path->pathauto) && !isset($entity->path->alias)) { - $entity->path->alias = ''; - } } /** @@ -154,82 +112,11 @@ function pathauto_entity_delete(EntityInterface $entity) { if ($entity->hasLinkTemplate('canonical')) { \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity); } - if ($entity instanceof ContentEntityInterface && $entity->hasField('path') && isset($entity->path->pathauto)) { - pathauto_entity_state_delete($entity); + if ($entity instanceof ContentEntityInterface && $entity->hasField('path')) { + $entity->path->first()->get('pathauto')->purge(); } } - -/** - * Load a pathauto state for an entity. - * - * @param string $entity_type - * An entity type. - * @param int $entity_id - * An entity ID. - * - * @return bool - * A value that evaluates to TRUE if Pathauto should control this entity's - * path. A value that evaluates to FALSE if Pathauto should not manage the - * entity's path. - */ -function pathauto_entity_state_load($entity_type, $entity_id) { - $pathauto_state = pathauto_entity_state_load_multiple($entity_type, array($entity_id)); - return !empty($pathauto_state) ? reset($pathauto_state) : FALSE; -} - -/** - * Load a pathauto state for multiple entities. - * - * @param string $entity_type - * The entity type. - * @param int[] $entity_ids - * The array of entity IDs. - * - * @return bool[] - * An array of Pathauto states keyed by entity ID. - */ -function pathauto_entity_state_load_multiple($entity_type, $entity_ids) { - return db_query("SELECT entity_id, pathauto FROM {pathauto_state} WHERE entity_type = :entity_type AND entity_id IN (:entity_ids[])", array(':entity_type' => $entity_type, ':entity_ids[]' => $entity_ids))->fetchAllKeyed(); -} - -/** - * Save the pathauto state for an entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity object. - * @param bool $pathauto_state - * A value that evaluates to TRUE means that Pathauto should keep controlling - * this entity's path in the future. A value that evaluates to FALSE means - * that Pathauto should not manage the entity's path. - */ -function pathauto_entity_state_save($entity, $pathauto_state) { - db_merge('pathauto_state') - ->key(array( - 'entity_type' => $entity->getEntityTypeId(), - 'entity_id' => $entity->id(), - )) - ->fields(array( - 'pathauto' => $pathauto_state ? 1 : 0, - )) - ->execute(); - drupal_static_reset('pathauto_entity_load'); -} - -/** - * Delete the pathauto state for an entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity object. - */ -function pathauto_entity_state_delete($entity) { - db_delete('pathauto_state') - ->condition('entity_type', $entity->getEntityTypeId()) - ->condition('entity_id', $entity->id()) - ->execute(); - drupal_static_reset('pathauto_entity_load'); -} - /** * Implements hook_field_info_alter(). */ @@ -255,6 +142,7 @@ function pathauto_entity_base_field_info(EntityTypeInterface $entity_type) { ->setCustomStorage(TRUE) ->setLabel(t('URL alias')) ->setTranslatable(TRUE) + ->setComputed(TRUE) ->setDisplayOptions('form', array( 'type' => 'path', 'weight' => 30, @@ -264,3 +152,14 @@ function pathauto_entity_base_field_info(EntityTypeInterface $entity_type) { return $fields; } } + +/** + * Implements hook_entity_base_field_info_alter(). + */ +function pathauto_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) { + if (isset($fields['path'])) { + // Path fields need to be computed so that the pathauto state can be + // accessed even if there is no alias being set. + $fields['path']->setComputed(TRUE); + } +} diff --git a/src/PathautoItem.php b/src/PathautoItem.php index 2e4ca77..f8ff754 100644 --- a/src/PathautoItem.php +++ b/src/PathautoItem.php @@ -7,6 +7,8 @@ namespace Drupal\pathauto; +use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\TypedData\DataDefinition; use Drupal\path\Plugin\Field\FieldType\PathItem; /** @@ -17,23 +19,46 @@ class PathautoItem extends PathItem { /** * {@inheritdoc} */ - public function insert() { - // Only allow the parent implementation to act if pathauto will not create - // an alias. - if (!isset($this->pathauto) || empty($this->pathauto)) { - parent::insert(); - } + public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { + $properties = parent::propertyDefinitions($field_definition); + $properties['pathauto'] = DataDefinition::create('integer') + ->setLabel(t('Pathauto state')) + ->setDescription(t('Whether an automated alias should be created or not.')) + ->setComputed(TRUE) + ->setClass('\Drupal\pathauto\PathautoState'); + return $properties; } /** * {@inheritdoc} */ - public function update() { + public function postSave($update) { // Only allow the parent implementation to act if pathauto will not create // an alias. - if (!isset($this->pathauto) || empty($this->pathauto)) { - parent::update(); + if ($this->pathauto == PathautoState::SKIP) { + parent::postSave($update); } + $this->get('pathauto')->persist(); + } + + /** + * {@inheritdoc} + */ + public function isEmpty() { + // Make sure that the pathauto state flag does not get lost if just that is + // changed. + return !$this->alias && !$this->get('pathauto')->hasValue(); } + /** + * {@inheritdoc} + */ + public function applyDefaultValue($notify = TRUE) { + parent::applyDefaultValue($notify); + // Created fields default creating a new alias. + $this->setValue(array('pathauto' => PathautoState::CREATE), $notify); + return $this; + } + + } diff --git a/src/PathautoManager.php b/src/PathautoManager.php index aa8a6dd..02c3604 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -263,12 +263,10 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } // Skip if pathauto processing is disabled. - if (isset($entity->path->pathauto) && empty($entity->path->pathauto) && empty($options['force'])) { + if ($entity->path->pathauto != PathautoState::CREATE && empty($options['force'])) { return NULL; } - pathauto_entity_state_save($entity, $entity->path->pathauto); - $options += array('language' => $entity->language()->getId()); $type = $entity->getEntityTypeId(); $bundle = $entity->bundle(); diff --git a/src/PathautoState.php b/src/PathautoState.php new file mode 100644 index 0000000..c591a17 --- /dev/null +++ b/src/PathautoState.php @@ -0,0 +1,121 @@ +value === NULL) { + $entity = $this->parent->getEntity(); + // If no value has been set or loaded yet, try to load a value if this + // entity has already been saved. + $this->value = \Drupal::keyValue($this->getCollection()) + ->get($this->parent->getEntity()->id()); + // If it was not yet saved or no value was found, try to detect based on + // an existing alias if the entity is not new. + if ($this->value === NULL) { + $entity_path = '/' . $entity->toUrl()->getInternalPath(); + $path = \Drupal::service('path.alias_manager') + ->getAliasByPath( + $entity_path, $entity->language()->getId() + ); + $pathauto_alias = \Drupal::service('pathauto.manager') + ->createAlias( + $entity->getEntityTypeId(), 'return', $entity_path, array( + $entity->getEntityType() + ->id() => $entity + ), $entity->bundle(), $entity->language()->getId() + ); + if (($path != $entity_path && $path == $pathauto_alias)) { + $this->value = static::CREATE; + } + else { + $this->value = static::SKIP; + } + } + } + return $this->value; + } + + /** + * {@inheritdoc} + */ + public + function setValue($value, $notify = TRUE) { + $this->value = $value; + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** + * Returns TRUE if a value was set. + */ + public + function hasValue() { + return $this->value !== NULL; + } + + /** + * Persists the state. + */ + public + function persist() { + \Drupal::keyValue($this->getCollection())->set( + $this->parent->getEntity() + ->id(), $this->value + ); + } + + /** + * Deletes the stored state. + */ + public + function purge() { + \Drupal::keyValue($this->getCollection()) + ->delete($this->parent->getEntity()->id()); + } + + /** + * @return string + */ + protected + function getCollection() { + return 'pathauto_state.' . $this->parent->getEntity() + ->getEntityTypeId(); + } +} diff --git a/src/PathautoWidget.php b/src/PathautoWidget.php index 4b36668..ed7ed09 100644 --- a/src/PathautoWidget.php +++ b/src/PathautoWidget.php @@ -48,18 +48,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen return $element; } - - if (!isset($entity->path->pathauto)) { - if (!$entity->isNew()) { - module_load_include('inc', 'pathauto'); - $path = \Drupal::service('path.alias_manager')->getAliasByPath('/' . $entity->urlInfo()->getInternalPath(), $entity->language()->getId()); - $pathauto_alias = \Drupal::service('pathauto.manager')->createAlias($entity->getEntityTypeId(), 'return', '/' . $entity->urlInfo()->getInternalPath(), array($entity->getEntityType()->id() => $entity), $entity->bundle(), $entity->language()->getId()); - $entity->path->pathauto = ($path != '/' . $entity->urlInfo()->getInternalPath() && $path == $pathauto_alias); - } - else { - $entity->path->pathauto = TRUE; - } - } $element['pathauto'] = array( '#type' => 'checkbox', '#title' => $this->t('Generate automatic URL alias'), @@ -76,9 +64,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // Override path.module's vertical tabs summary. $element['alias']['#attached']['library'] = ['pathauto/widget']; - if ($entity->path->pathauto && !empty($entity->old_alias) && empty($entity->path->alias)) { - $element['alias']['#default_value'] = $entity->old_alias; - $entity->path->alias = $entity->old_alias; + if ($entity->path->pathauto == PathautoState::CREATE && !empty($entity->path->old_alias) && empty($entity->path->alias)) { + $element['alias']['#default_value'] = $entity->path->old_alias; + $entity->path->alias = $entity->path->old_alias; } diff --git a/src/Plugin/Action/UpdateAction.php b/src/Plugin/Action/UpdateAction.php index fd7b102..58a8616 100644 --- a/src/Plugin/Action/UpdateAction.php +++ b/src/Plugin/Action/UpdateAction.php @@ -9,6 +9,7 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Action\ActionBase; use Drupal\Core\Session\AccountInterface; +use Drupal\pathauto\PathautoState; /** * Pathauto entity update action. @@ -24,8 +25,7 @@ class UpdateAction extends ActionBase { * {@inheritdoc} */ public function execute($entity = NULL) { - $entity->path = new \stdClass(); - $entity->path->pathauto = TRUE; + $entity->path->pathauto = PathautoState::CREATE; \Drupal::service('pathauto.manager')->updateAlias($entity, 'bulkupdate', array('message' => TRUE)); } diff --git a/src/Tests/PathautoBulkUpdateTest.php b/src/Tests/PathautoBulkUpdateTest.php index 8cc8e90..adb9afe 100644 --- a/src/Tests/PathautoBulkUpdateTest.php +++ b/src/Tests/PathautoBulkUpdateTest.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto\Tests; +use Drupal\pathauto\PathautoState; use Drupal\simpletest\WebTestBase; /** @@ -73,7 +74,11 @@ function testBulkUpdate() { 'update[user]' => TRUE, ); $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); - $this->assertText('Generated 7 URL aliases.'); // 5 nodes + 2 users + + // This has generated 6 aliases. 5 nodes and one user that we created. There + // is also UID 1 but that user was created before the path field existed, + // so he does not have a pathauto state. + $this->assertText('Generated 6 URL aliases.'); // Check that aliases have actually been created. foreach ($this->nodes as $node) { @@ -82,7 +87,7 @@ function testBulkUpdate() { $this->assertEntityAliasExists($this->adminUser); // Add a new node. - $new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => FALSE))); + $new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => PathautoState::SKIP))); // Run the update again which should not run against any nodes. $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 1a1d4f9..edab562 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\pathauto\PathautoState; use Drupal\simpletest\WebTestBase; /** @@ -65,7 +66,7 @@ function testLanguageAliases() { $this->saveAlias('/node/invalid', '/content/english-node', Language::LANGCODE_NOT_SPECIFIED); // Update the node, triggering a change in the English alias. - $node->path->pathauto = TRUE; + $node->path->pathauto = PathautoState::CREATE; $node->save(); // Check that the new English alias replaced the old one. diff --git a/src/Tests/PathautoMassDeleteTest.php b/src/Tests/PathautoMassDeleteTest.php index 318760c..99c3d41 100644 --- a/src/Tests/PathautoMassDeleteTest.php +++ b/src/Tests/PathautoMassDeleteTest.php @@ -161,7 +161,7 @@ function generateAliases() { // Check that we have aliases for the entities. foreach (array('nodes', 'accounts', 'terms') as $attribute) { foreach ($this->{$attribute} as $entity) { - $this->assertEntityAliasExists($node); + $this->assertEntityAliasExists($entity); } } } diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index d9884ce..4eda4ab 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -7,6 +7,7 @@ namespace Drupal\pathauto\Tests; use Drupal\node\Entity\Node; +use Drupal\pathauto\PathautoState; use Drupal\simpletest\WebTestBase; /** @@ -174,7 +175,7 @@ public function testNodeState() { 'title' => 'Node version one', 'type' => 'page', 'path' => array( - 'pathauto' => FALSE, + 'pathauto' => PathautoState::SKIP, ), )); @@ -187,7 +188,7 @@ public function testNodeState() { // Ensure that the pathauto field was saved to the database. \Drupal::entityManager()->getStorage('node')->resetCache(); $node = Node::load($node->id()); - $this->assertFalse($node->path->pathauto); + $this->assertIdentical($node->path->pathauto, PathautoState::SKIP); // Ensure that the manual path alias was saved and an automatic alias was not generated. $this->assertEntityAlias($node, '/test-alias'); @@ -228,19 +229,22 @@ public function testNodeState() { // Programatically save the node with an automatic alias. \Drupal::entityManager()->getStorage('node')->resetCache(); $node = Node::load($node->id()); - $node->path->pathauto = TRUE; + $node->path->pathauto = PathautoState::CREATE; $node->save(); // Ensure that the pathauto field was saved to the database. \Drupal::entityManager()->getStorage('node')->resetCache(); $node = Node::load($node->id()); - $this->assertTrue($node->path->pathauto); + $this->assertIdentical($node->path->pathauto, PathautoState::CREATE); $this->assertEntityAlias($node, '/content/node-version-three'); $this->assertNoEntityAliasExists($node, '/manually-edited-alias'); $this->assertNoEntityAliasExists($node, '/test-alias'); $this->assertNoEntityAliasExists($node, '/content/node-version-one'); $this->assertNoEntityAliasExists($node, '/content/node-version-two'); + + $node->delete(); + $this->assertNull(\Drupal::keyValue('pathauto_state.node')->get($node->id()), 'Pathauto state was deleted'); } } diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index c201864..092a361 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -74,8 +74,8 @@ public function assertAlias($source, $expected_alias, $langcode = Language::LANG break; } } - $this->assertIdentical($alias['alias'], $expected_alias, t("Alias for %source with language '@language' was %actual, expected %expected.", - array('%source' => $source, '%actual' => $alias['alias'], '%expected' => $expected_alias, '@language' => $langcode))); + $this->assertIdentical($alias['alias'], $expected_alias, t("Alias for %source with language '@language' is correct.", + array('%source' => $source, '@language' => $langcode))); } public function assertAliasExists($conditions) { diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index bb65bfc..2cd188f 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -11,6 +11,7 @@ use Drupal\Core\Language\Language; use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; +use Drupal\pathauto\PathautoState; use Drupal\simpletest\KernelTestBase; /** @@ -30,7 +31,6 @@ public function setUp() { parent::setup(); $this->installConfig(array('pathauto', 'taxonomy', 'system', 'node')); - $this->installSchema('pathauto', array('pathauto_state')); $this->installEntitySchema('user'); $this->installEntitySchema('node'); @@ -204,7 +204,7 @@ public function testUpdateActions() { $node = $this->drupalCreateNode(array('title' => 'First title')); $this->assertEntityAlias($node, '/content/first-title'); - $node->path->pathauto = TRUE; + $node->path->pathauto = PathautoState::CREATE; // Default action is PATHAUTO_UPDATE_ACTION_DELETE. $config->set('update_action', PathautoManagerInterface::UPDATE_ACTION_DELETE); From 40114631d51e4e1664b802286b394c4694e724ef Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 29 Nov 2015 12:42:51 +0100 Subject: [PATCH 130/169] Update after merge and rename pathauto.manager to pathauto.generator --- pathauto.module | 4 +-- pathauto.services.yml | 2 +- src/PathautoState.php | 28 ++++++------------- src/PathautoWidget.php | 2 +- src/Plugin/Action/UpdateAction.php | 2 +- .../AliasType/EntityAliasTypeBase.php | 2 +- src/Tests/PathautoNodeWebTest.php | 2 +- src/Tests/PathautoTestHelperTrait.php | 4 +-- src/Tests/PathautoUnitTest.php | 14 +++++----- 9 files changed, 25 insertions(+), 35 deletions(-) diff --git a/pathauto.module b/pathauto.module index 6103914..8974c30 100644 --- a/pathauto.module +++ b/pathauto.module @@ -95,14 +95,14 @@ function pathauto_entity_presave($entity) { * Implements hook_entity_insert(). */ function pathauto_entity_insert(EntityInterface $entity) { - \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'insert'); + \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'insert'); } /** * Implements hook_entity_update(). */ function pathauto_entity_update(EntityInterface $entity) { - \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'update'); + \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'update'); } diff --git a/pathauto.services.yml b/pathauto.services.yml index 7b17c15..cf7821e 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -1,5 +1,5 @@ services: - pathauto.manager: + pathauto.generator: class: Drupal\pathauto\PathautoGenerator arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation'] pathauto.alias_cleaner: diff --git a/src/PathautoState.php b/src/PathautoState.php index c591a17..ac63937 100644 --- a/src/PathautoState.php +++ b/src/PathautoState.php @@ -52,13 +52,8 @@ public function getValue() { ->getAliasByPath( $entity_path, $entity->language()->getId() ); - $pathauto_alias = \Drupal::service('pathauto.manager') - ->createAlias( - $entity->getEntityTypeId(), 'return', $entity_path, array( - $entity->getEntityType() - ->id() => $entity - ), $entity->bundle(), $entity->language()->getId() - ); + $pathauto_alias = \Drupal::service('pathauto.generator') + ->createEntityAlias($entity, 'return'); if (($path != $entity_path && $path == $pathauto_alias)) { $this->value = static::CREATE; } @@ -73,8 +68,7 @@ public function getValue() { /** * {@inheritdoc} */ - public - function setValue($value, $notify = TRUE) { + public function setValue($value, $notify = TRUE) { $this->value = $value; // Notify the parent of any changes. if ($notify && isset($this->parent)) { @@ -85,16 +79,14 @@ function setValue($value, $notify = TRUE) { /** * Returns TRUE if a value was set. */ - public - function hasValue() { + public function hasValue() { return $this->value !== NULL; } /** * Persists the state. */ - public - function persist() { + public function persist() { \Drupal::keyValue($this->getCollection())->set( $this->parent->getEntity() ->id(), $this->value @@ -104,18 +96,16 @@ function persist() { /** * Deletes the stored state. */ - public - function purge() { + public function purge() { \Drupal::keyValue($this->getCollection()) ->delete($this->parent->getEntity()->id()); } /** + * Returns the key value collection that should be used for the given entity. * @return string */ - protected - function getCollection() { - return 'pathauto_state.' . $this->parent->getEntity() - ->getEntityTypeId(); + protected function getCollection() { + return 'pathauto_state.' . $this->parent->getEntity()->getEntityTypeId(); } } diff --git a/src/PathautoWidget.php b/src/PathautoWidget.php index 0cfef26..053dc71 100644 --- a/src/PathautoWidget.php +++ b/src/PathautoWidget.php @@ -43,7 +43,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen - $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); + $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity); if (empty($pattern)) { return $element; } diff --git a/src/Plugin/Action/UpdateAction.php b/src/Plugin/Action/UpdateAction.php index cf797b3..75170eb 100644 --- a/src/Plugin/Action/UpdateAction.php +++ b/src/Plugin/Action/UpdateAction.php @@ -26,7 +26,7 @@ class UpdateAction extends ActionBase { */ public function execute($entity = NULL) { $entity->path->pathauto = PathautoState::CREATE; - \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'bulkupdate', array('message' => TRUE)); + \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', array('message' => TRUE)); } /** diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 3bb3f8a..ff1c8fd 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -240,7 +240,7 @@ protected function bulkUpdate(array $ids, array $options = array()) { $entities = $this->entityManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); foreach ($entities as $entity) { - \Drupal::service('pathauto.manager')->updateEntityAlias($entity, 'bulkupdate', $options); + \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', $options); } if (!empty($options['message'])) { diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index 31d9ced..41beeb7 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -128,7 +128,7 @@ function testNodeEditing() { foreach (PathautoPattern::loadMultiple($ids) as $pattern) { $pattern->delete(); } - \Drupal::service('pathauto.manager')->resetCaches(); + \Drupal::service('pathauto.generator')->resetCaches(); $this->drupalGet('node/add/article'); $this->assertNoFieldById('edit-path-0-pathauto'); diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 08d99c7..70e9f38 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -144,7 +144,7 @@ public function addTerm(VocabularyInterface $vocabulary, array $values = array() } public function assertEntityPattern($entity_type, $bundle, $langcode = Language::LANGCODE_NOT_SPECIFIED, $expected) { - \Drupal::service('pathauto.manager')->resetCaches(); + \Drupal::service('pathauto.generator')->resetCaches(); $values = [ 'langcode' => $langcode, @@ -152,7 +152,7 @@ public function assertEntityPattern($entity_type, $bundle, $langcode = Language: ]; $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create($values); - $pattern = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); + $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity); $this->assertIdentical($expected, $pattern->getPattern()); } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 57f1f92..687825f 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -190,7 +190,7 @@ public function testPatternLoadByEntity() { foreach ($tests as $test) { $entity = \Drupal::entityManager()->getStorage($test['entity'])->create($test['values']); $entity->save(); - $actual = \Drupal::service('pathauto.manager')->getPatternByEntity($entity); + $actual = \Drupal::service('pathauto.generator')->getPatternByEntity($entity); $this->assertIdentical($actual->getPattern(), $test['expected'], t("Correct pattern returned for @entity_type with @values", array( '@entity' => $test['entity'], '@values' => print_r($test['values'], TRUE), @@ -210,7 +210,7 @@ public function testCleanString() { $config->set('max_component_length', 35); $config->set('transliterate', TRUE); $config->save(); - \Drupal::service('pathauto.manager')->resetCaches(); + \Drupal::service('pathauto.generator')->resetCaches(); // Test the 'ignored words' removal. $tests['this'] = 'this'; @@ -250,7 +250,7 @@ public function testCleanAlias() { foreach ($tests as $input => $expected) { $output = \Drupal::service('pathauto.alias_cleaner')->cleanAlias($input); - $this->assertEqual($output, $expected, t("Drupal::service('pathauto.manager')->cleanAlias('@input') expected '@expected', actual '@output'", array( + $this->assertEqual($output, $expected, t("Drupal::service('pathauto.generator')->cleanAlias('@input') expected '@expected', actual '@output'", array( '@input' => $input, '@expected' => $expected, '@output' => $output, @@ -275,7 +275,7 @@ public function testPathDeleteMultiple() { } /** - * Test the different update actions in \Drupal::service('pathauto.manager')->createEntityAlias(). + * Test the different update actions in \Drupal::service('pathauto.generator')->createEntityAlias(). */ public function testUpdateActions() { $config = $this->config('pathauto.settings'); @@ -329,12 +329,12 @@ public function testUpdateActions() { // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'. $this->deleteAllAliases(); $node->setTitle('Sixth title'); - \Drupal::service('pathauto.manager')->updateEntityAlias($node, 'bulkupdate'); + \Drupal::service('pathauto.generator')->updateEntityAlias($node, 'bulkupdate'); $this->assertEntityAlias($node, '/content/sixth-title'); } /** - * Test that \Drupal::service('pathauto.manager')->createEntityAlias() will not create an alias for a pattern + * Test that \Drupal::service('pathauto.generator')->createEntityAlias() will not create an alias for a pattern * that does not get any tokens replaced. */ public function testNoTokensNoAlias() { @@ -408,7 +408,7 @@ function testNoExistingPathAliases() { $this->nodePattern ->setPattern('[node:title]') ->save(); - \Drupal::service('pathauto.manager')->resetCaches(); + \Drupal::service('pathauto.generator')->resetCaches(); // Check that Pathauto does not create an alias of '/admin'. $node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page')); From 8fbd8590832b0e9770fbef7fadff96ca34c5cec4 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 29 Nov 2015 14:10:06 +0100 Subject: [PATCH 131/169] Add helper method to test for defining conditions, fix tests, reset caches when saving patterns --- src/Entity/PathautoPattern.php | 12 +++++ src/Tests/PathautoMassDeleteTest.php | 6 ++- src/Tests/PathautoNodeWebTest.php | 1 - src/Tests/PathautoTestHelperTrait.php | 34 +++++++++++++-- src/Tests/PathautoUnitTest.php | 63 +++------------------------ 5 files changed, 55 insertions(+), 61 deletions(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 47ba165..38ebcb2 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -152,6 +152,9 @@ public function preSave(EntityStorageInterface $storage) { 'label' => $context->getContextDefinition()->getLabel() ]; } + + // Invalidate the static caches. + \Drupal::service('pathauto.generator')->resetCaches(); } /** @@ -179,6 +182,15 @@ public static function postLoad(EntityStorageInterface $storage, array &$entitie parent::postLoad($storage, $entities); } + /** + * {@inheritdoc} + */ + public static function postDelete(EntityStorageInterface $storage, array $entities) { + parent::postDelete($storage, $entities); + // Invalidate the static caches. + \Drupal::service('pathauto.generator')->resetCaches(); + } + /** * {@inheritdoc} */ diff --git a/src/Tests/PathautoMassDeleteTest.php b/src/Tests/PathautoMassDeleteTest.php index 99c3d41..b661f79 100644 --- a/src/Tests/PathautoMassDeleteTest.php +++ b/src/Tests/PathautoMassDeleteTest.php @@ -67,6 +67,10 @@ function setUp() { ); $this->adminUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->adminUser); + + $this->createPattern('node', '/content/[node:title]'); + $this->createPattern('user', '/users/[user:name]'); + $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]'); } /** @@ -87,7 +91,7 @@ function testDeleteAll() { // 2. Test deleting only specific (entity type) aliases. $manager = $this->container->get('plugin.manager.alias_type'); - $pathauto_plugins = array('node' => 'nodes', 'taxonomy_term' => 'terms', 'user' => 'accounts'); + $pathauto_plugins = array('canonical_entities:node' => 'nodes', 'canonical_entities:taxonomy_term' => 'terms', 'canonical_entities:user' => 'accounts'); foreach ($pathauto_plugins as $pathauto_plugin => $attribute) { $this->generateAliases(); $edit = array( diff --git a/src/Tests/PathautoNodeWebTest.php b/src/Tests/PathautoNodeWebTest.php index 41beeb7..83a3fb5 100644 --- a/src/Tests/PathautoNodeWebTest.php +++ b/src/Tests/PathautoNodeWebTest.php @@ -128,7 +128,6 @@ function testNodeEditing() { foreach (PathautoPattern::loadMultiple($ids) as $pattern) { $pattern->delete(); } - \Drupal::service('pathauto.generator')->resetCaches(); $this->drupalGet('node/add/article'); $this->assertNoFieldById('edit-path-0-pathauto'); diff --git a/src/Tests/PathautoTestHelperTrait.php b/src/Tests/PathautoTestHelperTrait.php index 70e9f38..01704d5 100644 --- a/src/Tests/PathautoTestHelperTrait.php +++ b/src/Tests/PathautoTestHelperTrait.php @@ -12,6 +12,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Render\BubbleableMetadata; use Drupal\pathauto\Entity\PathautoPattern; +use Drupal\pathauto\PathautoPatternInterface; use Drupal\taxonomy\VocabularyInterface; /** @@ -24,22 +25,50 @@ trait PathautoTestHelperTrait { * * @param string $entity_type_id * The entity type. - * @param $pattern + * @param string $pattern * The path pattern. + * @param int $weight + * (optional) The pattern weight. * * @return \Drupal\pathauto\PathautoPatternInterface * The created pattern. */ - protected function createPattern($entity_type_id, $pattern) { + protected function createPattern($entity_type_id, $pattern, $weight = 10) { $pattern = PathautoPattern::create([ 'id' => Unicode::strtolower($this->randomMachineName()), 'type' => 'canonical_entities:' . $entity_type_id, 'pattern' => $pattern, + 'weight' => $weight, ]); $pattern->save(); return $pattern; } + /** + * Add a bundle condition to a pathauto pattern. + * + * @param \Drupal\pathauto\PathautoPatternInterface $pattern + * The pattern. + * @param string $entity_type + * The entity type ID. + * @param string $bundle + * The bundle + */ + protected function addBundleCondition(PathautoPatternInterface $pattern, $entity_type, $bundle) { + $pattern->addSelectionCondition( + [ + 'id' => 'entity_bundle:' . $entity_type, + 'bundles' => [ + $bundle => $bundle, + ], + 'negate' => FALSE, + 'context_mapping' => [ + $entity_type => $entity_type, + ] + ] + ); + } + public function assertToken($type, $object, $token, $expected) { $bubbleable_metadata = new BubbleableMetadata(); $tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object), [], $bubbleable_metadata); @@ -144,7 +173,6 @@ public function addTerm(VocabularyInterface $vocabulary, array $values = array() } public function assertEntityPattern($entity_type, $bundle, $langcode = Language::LANGCODE_NOT_SPECIFIED, $expected) { - \Drupal::service('pathauto.generator')->resetCaches(); $values = [ 'langcode' => $langcode, diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 687825f..9432e7d 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -79,35 +79,12 @@ public function testGetSchemaAliasMaxLength() { * Test pathauto_pattern_load_by_entity(). */ public function testPatternLoadByEntity() { - $pattern = $this->createPattern('node', '/article/[node:title]'); - $pattern->addSelectionCondition( - [ - 'id' => 'entity_type:node_type', - 'bundles' => [ - 'article' => 'article', - ], - 'negate' => FALSE, - 'context_mapping' => [ - 'node' => 'node', - ] - ] - ); - $pattern->setWeight(-1); + $pattern = $this->createPattern('node', '/article/[node:title]', -1); + $this->addBundleCondition($pattern, 'node', 'article'); $pattern->save(); - $pattern = $this->createPattern('node', '/article/en/[node:title]'); - $pattern->addSelectionCondition( - [ - 'id' => 'entity_type:node_type', - 'bundles' => [ - 'article' => 'article', - ], - 'negate' => FALSE, - 'context_mapping' => [ - 'node' => 'node', - ] - ] - ); + $pattern = $this->createPattern('node', '/article/en/[node:title]', -2); + $this->addBundleCondition($pattern, 'node', 'article'); $pattern->addSelectionCondition( [ 'id' => 'language', @@ -124,23 +101,10 @@ public function testPatternLoadByEntity() { $new_definition = new ContextDefinition('language', 'Language'); $new_context = new Context($new_definition); $pattern->addContext('node:langcode:language', $new_context); - $pattern->setWeight(-2); $pattern->save(); $pattern = $this->createPattern('node', '/[node:title]'); - $pattern->addSelectionCondition( - [ - 'id' => 'entity_type:node_type', - 'bundles' => [ - 'page' => 'page', - ], - 'negate' => FALSE, - 'context_mapping' => [ - 'node' => 'node', - ] - ] - ); - $pattern->setWeight(-1); + $this->addBundleCondition($pattern, 'node', 'page'); $pattern->save(); $tests = array( @@ -374,20 +338,8 @@ public function testEntityBundleDeleting() { // Create a vocabulary and test that it's pattern variable works. $vocab = $this->addVocabulary(array('vid' => 'name')); $this->createPattern('taxonomy_term', 'base'); - $pattern = $this->createPattern('taxonomy_term', 'bundle'); - $pattern->addSelectionCondition( - [ - 'id' => 'entity_type:taxonomy_vocabulary', - 'bundles' => [ - 'name' => 'name', - ], - 'negate' => FALSE, - 'context_mapping' => [ - 'taxonomy_term' => 'taxonomy_term', - ] - ] - ); - $pattern->setWeight(1); + $pattern = $this->createPattern('taxonomy_term', 'bundle', -1); + $this->addBundleCondition($pattern, 'taxonomy_term', 'name'); $pattern->save(); @@ -408,7 +360,6 @@ function testNoExistingPathAliases() { $this->nodePattern ->setPattern('[node:title]') ->save(); - \Drupal::service('pathauto.generator')->resetCaches(); // Check that Pathauto does not create an alias of '/admin'. $node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page')); From 83bf0067cf29c7ebf73903b35ebb6c7fa7a4678b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 29 Nov 2015 17:32:26 +0100 Subject: [PATCH 132/169] Improve deriver --- src/Plugin/Deriver/EntityAliasTypeDeriver.php | 25 ++++++++++++++++--- .../AliasType/EntityAliasTypeBase.php | 5 ++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Plugin/Deriver/EntityAliasTypeDeriver.php b/src/Plugin/Deriver/EntityAliasTypeDeriver.php index 3d272cf..58a2abf 100644 --- a/src/Plugin/Deriver/EntityAliasTypeDeriver.php +++ b/src/Plugin/Deriver/EntityAliasTypeDeriver.php @@ -6,22 +6,39 @@ namespace Drupal\pathauto\Plugin\Deriver; +use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\ctools\Plugin\Deriver\EntityDeriverBase; +/** + * Deriver that exposes content entities as alias type plugins. + */ class EntityAliasTypeDeriver extends EntityDeriverBase { + + /** + * {@inheritdoc} + */ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { - if ($entity_type->hasLinkTemplate('canonical')) { + // An entity type must have a canonical link template and support fields. + if ($entity_type->hasLinkTemplate('canonical') && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class)) { + $base_fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id); + if (!isset($base_fields['path'])) { + // The entity type does not have a path field and is therefore not + // supported. + // @todo: Add a UI to enable that base field on any content entity. + continue; + } $this->derivatives[$entity_type_id] = $base_plugin_definition; - $this->derivatives[$entity_type_id]['label'] = $this->t('@label', ['@label' => $entity_type->getLabel()]); + $this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel(); $this->derivatives[$entity_type_id]['types'] = [$entity_type_id]; $this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider(); $this->derivatives[$entity_type_id]['context'] = [ - "$entity_type_id" => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()])) + $entity_type_id => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()])) ]; } } return $this->derivatives; } -} \ No newline at end of file + +} diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index ff1c8fd..abd95fa 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; @@ -270,9 +271,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s * {@inheritdoc} */ public function applies($object) { - $definition = $this->entityManager->getDefinition($this->getEntityTypeId()); - $class = $definition->getClass(); - return ($object instanceof $class); + return $object instanceof FieldableEntityInterface && $object->getEntityTypeId() == $this->getEntityTypeId(); } /** From 53694dacd47f48bbd6bdeac17ce7f9c91bbe262d Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 5 Dec 2015 15:20:11 +0100 Subject: [PATCH 133/169] Remove wizards, provide a simple pattern edit form, list builder improvements --- pathauto.routing.yml | 73 +---- src/AliasTypeInterface.php | 5 +- src/Entity/PathautoPattern.php | 25 +- src/Form/AddContext.php | 64 ---- src/Form/ConfigurePatternForm.php | 69 ----- src/Form/ContextConfigure.php | 70 ----- src/Form/ContextDelete.php | 51 ---- src/Form/CriteriaDelete.php | 60 ---- src/Form/CriteriaForm.php | 65 ---- src/Form/PatternEditForm.php | 278 ++++++++++++++++++ src/Form/SelectionCriteriaForm.php | 73 ----- src/PathautoPatternListBuilder.php | 11 +- .../AliasType/EntityAliasTypeBase.php | 92 +----- .../pathauto/AliasType/ForumAliasType.php | 7 - src/Wizard/PatternWizard.php | 73 ----- src/Wizard/PatternWizardAdd.php | 21 -- 16 files changed, 307 insertions(+), 730 deletions(-) delete mode 100644 src/Form/AddContext.php delete mode 100644 src/Form/ConfigurePatternForm.php delete mode 100644 src/Form/ContextConfigure.php delete mode 100644 src/Form/ContextDelete.php delete mode 100644 src/Form/CriteriaDelete.php delete mode 100644 src/Form/CriteriaForm.php create mode 100644 src/Form/PatternEditForm.php delete mode 100644 src/Form/SelectionCriteriaForm.php delete mode 100644 src/Wizard/PatternWizard.php delete mode 100644 src/Wizard/PatternWizardAdd.php diff --git a/pathauto.routing.yml b/pathauto.routing.yml index d280ba4..0902381 100644 --- a/pathauto.routing.yml +++ b/pathauto.routing.yml @@ -9,83 +9,12 @@ entity.pathauto_pattern.collection: entity.pathauto_pattern.add_form: path: '/admin/config/search/path/patterns/add' defaults: - _entity_wizard: 'pathauto_pattern.add' + _entity_form: 'pathauto_pattern.default' _title: 'Add Pathauto pattern' tempstore_id: 'pathauto.pattern' requirements: _permission: 'administer pathauto' -entity.pathauto_pattern.edit_form: - path: '/admin/config/search/path/patterns/{machine_name}/{step}' - defaults: - _entity_wizard: 'pathauto_pattern.edit' - _title: 'Edit Pathauto pattern' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -entity.pathauto_pattern.delete_form: - path: '/admin/config/search/path/patterns/{pathauto_pattern}/delete' - defaults: - _entity_form: 'pathauto_pattern.delete' - _title: 'Delete Pathauto pattern' - requirements: - _entity_access: pathauto_pattern.delete - -pathauto.pattern.relationship.add: - path: '/admin/config/search/path/patterns/{machine_name}/relationship/{context}/add' - defaults: - _form: '\Drupal\pathauto\Form\ContextConfigure' - _title: 'Configure relationship' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -pathauto.pattern.relationship.edit: - path: '/admin/config/search/path/patterns/{machine_name}/relationship/{context}/edit' - defaults: - _form: '\Drupal\pathauto\Form\ContextConfigure' - _title: 'Configure relationship' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -pathauto.pattern.relationship.delete: - path: '/admin/config/search/path/patterns/{machine_name}/relationship/{id}/delete' - defaults: - _form: '\Drupal\pathauto\Form\ContextDelete' - _title: 'Delete relationship' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -pathauto.pattern.condition.add: - path: '/admin/config/search/path/patterns/{machine_name}/criteria/{condition}/add' - defaults: - _form: '\Drupal\pathauto\Form\CriteriaForm' - _title: 'Configure selection criteria' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -pathauto.pattern.condition.edit: - path: '/admin/config/search/path/patterns/{machine_name}/criteria/{condition}/edit' - defaults: - _form: '\Drupal\pathauto\Form\CriteriaForm' - _title: 'Configure selection criteria' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - -pathauto.pattern.condition.delete: - path: '/admin/config/search/path/patterns/{machine_name}/criteria/{id}/delete' - defaults: - _form: '\Drupal\pathauto\Form\CriteriaDelete' - _title: 'Delete selection criteria' - tempstore_id: 'pathauto.pattern' - requirements: - _permission: 'administer pathauto' - pathauto.settings.form: path: '/admin/config/search/path/settings' defaults: diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index f1bc67c..80d9319 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -7,14 +7,13 @@ namespace Drupal\pathauto; -use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Component\Plugin\DerivativeInspectionInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface; -use Drupal\Core\Plugin\PluginFormInterface; /** * Provides an interface for pathauto alias types. */ -interface AliasTypeInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface, PluginFormInterface { +interface AliasTypeInterface extends ContextAwarePluginInterface, DerivativeInspectionInterface { /** * Get the label. diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 38ebcb2..f73402c 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -26,12 +26,12 @@ * handlers = { * "list_builder" = "Drupal\pathauto\PathautoPatternListBuilder", * "form" = { + * "default" = "Drupal\pathauto\Form\PatternEditForm", * "delete" = "Drupal\Core\Entity\EntityDeleteForm" * }, - * "wizard" = { - * "add" = "Drupal\pathauto\Wizard\PatternWizardAdd", - * "edit" = "Drupal\pathauto\Wizard\PatternWizard" - * } + * "route_provider" = { + * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", + * }, * }, * config_prefix = "pattern", * admin_permission = "administer pathauto", @@ -46,7 +46,7 @@ * }, * links = { * "collection" = "/admin/config/search/path/patterns", - * "edit-form" = "/admin/config/search/path/patterns/{machine_name}/{step}", + * "edit-form" = "/admin/config/search/path/patterns/{pathauto_pattern}", * "delete-form" = "/admin/config/search/path/patterns/{pathauto_pattern}/delete" * } * ) @@ -206,21 +206,6 @@ public function calculateDependencies() { return $this->getDependencies(); } - /** - * {@inheritdoc} - */ - protected function urlRouteParameters($rel) { - $uri_route_parameters = parent::urlRouteParameters($rel); - // @todo Improve detection, check the path string? - if (in_array($rel, ['edit-form', 'config-translation-overview'])) { - $uri_route_parameters = [ - 'machine_name' => $this->id(), - 'step' => 'general', - ]; - } - return $uri_route_parameters; - } - /** * {@inheritdoc} */ diff --git a/src/Form/AddContext.php b/src/Form/AddContext.php deleted file mode 100644 index 9f6c048..0000000 --- a/src/Form/AddContext.php +++ /dev/null @@ -1,64 +0,0 @@ -getContexts(); - } - - /** - * {@inheritdoc} - */ - protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row) { - return ['pathauto.pattern.relationship', ['machine_name' => $machine_name, 'context' => $row]]; - } - -} diff --git a/src/Form/ConfigurePatternForm.php b/src/Form/ConfigurePatternForm.php deleted file mode 100644 index db6dc45..0000000 --- a/src/Form/ConfigurePatternForm.php +++ /dev/null @@ -1,69 +0,0 @@ -getTemporaryValue('wizard'); - /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ - $pathauto_pattern = $cached_values['pathauto_pattern']; - $aliasType = $pathauto_pattern->getAliasType(); - $form = $aliasType->buildConfigurationForm($form, $form_state); - $form['default']['#default_value'] = $pathauto_pattern->getPattern(); - // This is rough but generally seems right. I think Token is not nuanced - // enough to handle this. - $tokens = $form['default']['#token_types']; - $contexts = $pathauto_pattern->getContexts(); - foreach ($contexts as $context_id => $context) { - // @todo Figure out what to do with non-entity contexts. - if (strpos($context->getContextDefinition()->getDataType(), ':') === FALSE) { - continue; - } - list($data_type, $entity_type) = explode(':', $context->getContextDefinition()->getDataType()); - if ($data_type == 'entity') { - if ($entity_type == 'taxonomy_term') { - $entity_type = 'term'; - } - if (!in_array($entity_type, $tokens)) { - $tokens[] = $entity_type; - } - } - } - - $form['default']['#token_types'] = $tokens; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $cached_values = $form_state->getTemporaryValue('wizard'); - /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ - $pathauto_pattern = $cached_values['pathauto_pattern']; - $pathauto_pattern->setPattern($form_state->getValue('default')); - } - -} diff --git a/src/Form/ContextConfigure.php b/src/Form/ContextConfigure.php deleted file mode 100644 index 2001999..0000000 --- a/src/Form/ContextConfigure.php +++ /dev/null @@ -1,70 +0,0 @@ -tempstore->get($this->tempstore_id)->get($this->machine_name); - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - if (!$pattern->hasContext($form_state->getValue('id'))) { - /** @var \Drupal\Core\Plugin\Context\ContextInterface $context */ - $context = $form_state->getValue('context_object'); - $definition = $context->getContextDefinition(); - $new_definition = new ContextDefinition($definition->getDataType(), $form_state->getValue('label'), $definition->isRequired(), $definition->isMultiple(), $definition->getDescription(), $definition->getDefaultValue()); - $new_context = new Context($new_definition, $context->hasContextValue() ? $context->getContextValue() : NULL); - $pattern->addContext($form_state->getValue('id'), $new_context); - } - else { - $context = $pattern->getContext($form_state->getValue('id')); - $definition = $context->getContextDefinition(); - $new_definition = new ContextDefinition($definition->getDataType(), $form_state->getValue('label'), $definition->isRequired(), $definition->isMultiple(), $definition->getDescription(), $definition->getDefaultValue()); - $new_context = new Context($new_definition, $context->hasContextValue() ? $context->getContextValue() : NULL); - $pattern->replaceContext($form_state->getValue('id'), $new_context); - } - $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); - parent::submitForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - protected function getParentRouteInfo($cached_values) { - return ['entity.pathauto_pattern.edit_form', ['machine_name' => $this->machine_name, 'step' => 'contexts']]; - } - - /** - * {@inheritdoc} - */ - protected function setContexts($cached_values, $contexts) {} - - /** - * {@inheritdoc} - */ - protected function getContexts($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - return $pattern->getContexts(); - } - -} \ No newline at end of file diff --git a/src/Form/ContextDelete.php b/src/Form/ContextDelete.php deleted file mode 100644 index 58c82ec..0000000 --- a/src/Form/ContextDelete.php +++ /dev/null @@ -1,51 +0,0 @@ - $this->machine_name, 'step' => 'contexts']); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);; - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - $pattern->removeContext($this->id); - $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); - parent::submitForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function getContexts($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - return $pattern->getContexts(); - } - -} diff --git a/src/Form/CriteriaDelete.php b/src/Form/CriteriaDelete.php deleted file mode 100644 index 6463d55..0000000 --- a/src/Form/CriteriaDelete.php +++ /dev/null @@ -1,60 +0,0 @@ - $this->machine_name, 'step' => 'selection_criteria']]; - } - - /** - * {@inheritdoc} - */ - protected function getConditions($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - $conditions = []; - foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { - $conditions[$uuid] = $configuration->getConfiguration(); - } - return $conditions; - } - - /** - * {@inheritdoc} - */ - protected function setConditions($cached_values, $conditions) { - $old_conditions = $this->getConditions($cached_values); - $diff = array_diff(array_keys($old_conditions), array_keys($conditions)); - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - // There should only be one item in $diff, but we'll loop anyway. - foreach ($diff as $key) { - $pattern->removeSelectionCondition($key); - } - return $cached_values; - } - - /** - * {@inheritdoc} - */ - protected function getContexts($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - return $pattern->getAliasType()->getContexts(); - } - -} diff --git a/src/Form/CriteriaForm.php b/src/Form/CriteriaForm.php deleted file mode 100644 index 7d6575c..0000000 --- a/src/Form/CriteriaForm.php +++ /dev/null @@ -1,65 +0,0 @@ - $this->machine_name, 'step' => 'selection_criteria']]; - } - - /** - * {@inheritdoc} - */ - protected function getRouteInfo($condition) { - return ['pathauto.pattern.condition.add', ['machine_name' => $this->machine_name, 'condition' => $condition]]; - } - - /** - * {@inheritdoc} - */ - protected function getConditions($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - $conditions = []; - foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { - $conditions[$uuid] = $configuration->getConfiguration(); - } - return $conditions; - } - - /** - * {@inheritdoc} - */ - protected function setConditions($cached_values, $conditions) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - // Set all the old conditions again since this is kind of indiscriminate. - foreach ($conditions as $id => $configuration) { - $pattern->getSelectionConditions()->setInstanceConfiguration($id, $conditions[$id]); - } - return $cached_values; - } - - /** - * {@inheritdoc} - */ - protected function getContexts($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - return $pattern->getContexts(); - } - -} \ No newline at end of file diff --git a/src/Form/PatternEditForm.php b/src/Form/PatternEditForm.php new file mode 100644 index 0000000..c0e457d --- /dev/null +++ b/src/Form/PatternEditForm.php @@ -0,0 +1,278 @@ +get('plugin.manager.alias_type'), + $container->get('entity_type.bundle.info'), + $container->get('entity_type.manager'), + $container->get('language_manager') + ); + } + + /** + * PatternEditForm constructor. + * + * @param \Drupal\pathauto\AliasTypeManager $manager + * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + */ + function __construct(AliasTypeManager $manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager) { + $this->manager = $manager; + $this->entityTypeBundleInfo = $entity_type_bundle_info; + $this->entityTypeManager = $entity_type_manager; + $this->languageManager= $language_manager; + } + + /** + * {@inheritDoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + + $options = []; + foreach ($this->manager->getDefinitions() as $plugin_id => $plugin_definition) { + $options[$plugin_id] = $plugin_definition['label']; + } + $form['type'] = [ + '#type' => 'select', + '#title' => $this->t('Pattern type'), + '#default_value' => $this->entity->getType(), + '#options' => $options, + '#required' => TRUE, + '#limit_validation_errors' => array(array('type')), + '#submit' => array('::submitSelectType'), + '#executes_submit_callback' => TRUE, + '#ajax' => array( + 'callback' => '::ajaxReplacePatternForm', + 'wrapper' => 'pathauto-pattern', + 'method' => 'replace', + ), + ]; + + $form['pattern_container'] = [ + '#type' => 'container', + '#prefix' => '
', + '#suffix' => '
', + ]; + + // if there is no type yet, stop here. + if ($this->entity->getType()) { + + $alias_type = $this->entity->getAliasType(); + + $form['pattern_container']['pattern'] = array( + '#type' => 'textfield', + '#title' => 'Path pattern', + '#default_value' => $this->entity->getPattern(), + '#size' => 65, + '#maxlength' => 1280, + '#element_validate' => array('token_element_validate'), + '#after_build' => array('token_element_validate'), + '#token_types' => $alias_type->getTokenTypes(), + '#min_tokens' => 1, + ); + + // Show the token help relevant to this pattern type. + $form['pattern_container']['token_help'] = array( + '#theme' => 'token_tree', + '#token_types' => $alias_type->getTokenTypes(), + '#dialog' => TRUE, + ); + + // Expose bundle and language conditions. + if ($alias_type->getDerivativeId() && $entity_type = $this->entityTypeManager->getDefinition($alias_type->getDerivativeId())) { + + $default_bundles = []; + $default_languages = []; + foreach ($this->entity->getSelectionConditions() as $condition_id => $condition) { + if ($condition->getPluginId() == 'entity_bundle:' . $entity_type->id()) { + $default_bundles = $condition->getConfiguration()['bundles']; + } + elseif ($condition->getPluginId() == 'language') { + $default_languages = $condition->getConfiguration()['langcodes']; + } + } + + if ($bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id())) { + $bundle_options = []; + foreach ($bundles as $id => $info) { + $bundle_options[$id] = $info['label']; + } + $form['pattern_container']['bundles'] = array( + '#title' => $entity_type->getBundleLabel(), + '#type' => 'checkboxes', + '#options' => $bundle_options, + '#default_value' => $default_bundles, + '#description' => t('Check to which types this pattern should be applied. Leave empty to allow any.'), + ); + } + + if ($this->languageManager->isMultilingual() && $entity_type->isTranslatable()) { + $language_options = []; + foreach ($this->languageManager->getLanguages() as $id => $language) { + $language_options[$id] = $language->getName(); + } + $form['pattern_container']['languages'] = array( + '#title' => $this->t('Languages'), + '#type' => 'checkboxes', + '#options' => $language_options, + '#default_value' => $default_languages, + '#description' => t('Check to which languages this pattern should be applied. Leave empty to allow any.'), + ); + } + } + } + + $form['label'] = array( + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#maxlength' => 255, + '#default_value' => $this->entity->label(), + '#required' => TRUE, + ); + + $form['id'] = array( + '#type' => 'machine_name', + '#title' => $this->t('ID'), + '#maxlength' => 255, + '#default_value' => $this->entity->id(), + '#required' => TRUE, + '#disabled' => !$this->entity->isNew(), + '#machine_name' => array( + 'exists' => 'Drupal\pathauto\Entity\PathautoPattern::load', + ), + ); + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritDoc} + */ + public function buildEntity(array $form, FormStateInterface $form_state) { + /** @var \Drupal\pathauto\PathautoPatternInterface $entity */ + $entity = parent::buildEntity($form, $form_state); + + $default_weight = 0; + + $alias_type = $entity->getAliasType(); + if ($alias_type->getDerivativeId() && $this->entityTypeManager->hasDefinition($alias_type->getDerivativeId())) { + $entity_type = $alias_type->getDerivativeId(); + // First, remove bundle and language conditions. + foreach ($entity->getSelectionConditions() as $condition_id => $condition) { + + if ($condition->getPluginId() == 'entity_bundle:' . $entity_type || $condition->getPluginId() == 'language') { + $entity->removeSelectionCondition($condition_id); + } + } + + if ($bundles = array_filter((array) $form_state->getValue('bundles'))) { + $default_weight -= 5; + $entity->addSelectionCondition( + [ + 'id' => 'entity_bundle:' . $entity_type, + 'bundles' => $bundles, + 'negate' => FALSE, + 'context_mapping' => [ + $entity_type => $entity_type, + ] + ] + ); + } + + if ($languages = array_filter((array) $form_state->getValue('languages'))) { + $default_weight -= 5; + $entity->addSelectionCondition( + [ + 'id' => 'language', + 'langcodes' => array_combine($languages, $languages), + 'negate' => FALSE, + 'context_mapping' => [ + 'language' => $entity_type . ':' . $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode') . ':language', + ] + ] + ); + } + + } + + $entity->setWeight($default_weight); + + return $entity; + } + + /** + * {@inheritDoc} + */ + public function save(array $form, FormStateInterface $form_state) { + parent::save($form, $form_state); + drupal_set_message($this->t('Pattern @label saved.', ['@label' => $this->entity->label()])); + $form_state->setRedirectUrl($this->entity->toUrl('collection')); + } + + /** + * Handles switching the type selector. + */ + public function ajaxReplacePatternForm($form, FormStateInterface $form_state) { + return $form['pattern_container']; + } + + /** + * Handles submit call when alias type is selected. + */ + public function submitSelectType(array $form, FormStateInterface $form_state) { + $this->entity = $this->buildEntity($form, $form_state); + $form_state->setRebuild(); + } + +} diff --git a/src/Form/SelectionCriteriaForm.php b/src/Form/SelectionCriteriaForm.php deleted file mode 100644 index a751831..0000000 --- a/src/Form/SelectionCriteriaForm.php +++ /dev/null @@ -1,73 +0,0 @@ - $machine_name, 'condition' => $row]]; - } - - /** - * {@inheritdoc} - */ - protected function getConditions($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - $conditions = []; - foreach ($pattern->getSelectionConditions() as $uuid => $configuration) { - $conditions[$uuid] = $configuration->getConfiguration(); - } - return $conditions; - } - - /** - * {@inheritdoc} - */ - protected function getContexts($cached_values) { - /** @var \Drupal\pathauto\PathautoPatternInterface $pattern */ - $pattern = $cached_values['pathauto_pattern']; - return $pattern->getContexts(); - } - -} diff --git a/src/PathautoPatternListBuilder.php b/src/PathautoPatternListBuilder.php index 87edc8e..a4b42ac 100644 --- a/src/PathautoPatternListBuilder.php +++ b/src/PathautoPatternListBuilder.php @@ -26,9 +26,10 @@ public function getFormId() { * {@inheritdoc} */ public function buildHeader() { - $header['label'] = $this->t('Pathauto pattern'); - $header['id'] = $this->t('Machine name'); + $header['label'] = $this->t('Label'); + $header['pattern'] = $this->t('Pattern'); $header['type'] = $this->t('Pattern type'); + $header['conditions'] = $this->t('Conditions'); return $header + parent::buildHeader(); } @@ -38,8 +39,12 @@ public function buildHeader() { public function buildRow(EntityInterface $entity) { /* @var \Drupal\pathauto\PathautoPatternInterface $entity */ $row['label'] = $entity->label(); - $row['id']['#markup'] = $entity->id(); + $row['patern']['#markup'] = $entity->getPattern(); $row['type']['#markup'] = $entity->getAliasType()->getLabel(); + $row['conditions']['#theme'] = 'item_list'; + foreach ($entity->getSelectionConditions() as $condition) { + $row['conditions']['#items'][] = $condition->summary(); + } return $row + parent::buildRow($entity); } diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index abd95fa..7868ad1 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -7,11 +7,9 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; -use Drupal\Component\Utility\NestedArray; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContextAwarePluginBase; @@ -46,9 +44,9 @@ class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInt /** * The entity manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * The path prefix for this entity type. @@ -70,15 +68,14 @@ class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInt * The module handler service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity manager service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->moduleHandler = $module_handler; $this->languageManager = $language_manager; - $this->entityManager = $entity_manager; - $this->setConfiguration($configuration); + $this->entityTypeManager = $entity_type_manager; } /** @@ -91,34 +88,10 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('module_handler'), $container->get('language_manager'), - $container->get('entity.manager') + $container->get('entity_type.manager') ); } - /** - * {@inheritdoc} - */ - public function getConfiguration() { - return $this->configuration; - } - - /** - * {@inheritdoc} - */ - public function setConfiguration(array $configuration) { - $this->configuration = NestedArray::mergeDeep( - $this->defaultConfiguration(), - $configuration - ); - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return array(); - } - /** * {@inheritdoc} */ @@ -143,36 +116,6 @@ public function getTokenTypes() { return $definition['types']; } - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $form = array( - '#type' => 'details', - '#title' => $this->getLabel(), - ); - - $form['default'] = array( - '#type' => 'textfield', - '#title' => 'Path pattern', - '#default_value' => !empty($this->configuration['default']) ? $this->configuration['default'] : '', - '#size' => 65, - '#maxlength' => 1280, - '#element_validate' => array('token_element_validate'), - '#after_build' => array('token_element_validate'), - '#token_types' => $this->getTokenTypes(), - '#min_tokens' => 1, - ); - - // Show the token help relevant to this pattern type. - $form['token_help'] = array( - '#theme' => 'token_tree', - '#token_types' => $this->getTokenTypes(), - '#dialog' => TRUE, - ); - return $form; - } - /** * {@inheritdoc} */ @@ -182,7 +125,7 @@ public function batchUpdate(&$context) { $context['sandbox']['current'] = 0; } - $entity_type = $this->entityManager->getDefinition($this->getEntityTypeId()); + $entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId()); $id_key = $entity_type->getKey('id'); $query = db_select($entity_type->get('base_table'), 'base_table'); @@ -239,7 +182,7 @@ protected function getEntityTypeId() { protected function bulkUpdate(array $ids, array $options = array()) { $options += array('message' => FALSE); - $entities = $this->entityManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); + $entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); foreach ($entities as $entity) { \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', $options); } @@ -253,18 +196,9 @@ protected function bulkUpdate(array $ids, array $options = array()) { * {@inheritdoc} */ public function calculateDependencies() { - } - - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { - } - - /** - * {@inheritdoc} - */ - public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $dependencies = []; + $dependencies['module'][] = $this->entityTypeManager->getDefinition($this->getEntityTypeId())->getProvider(); + return $dependencies; } /** @@ -279,7 +213,7 @@ public function applies($object) { */ public function getSourcePrefix() { if (empty($this->prefix)) { - $entity_type = $this->entityManager->getDefinition($this->getEntityTypeId()); + $entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId()); $path = $entity_type->getLinkTemplate('canonical'); $this->prefix = substr($path, 0, strpos($path, '{')); } diff --git a/src/Plugin/pathauto/AliasType/ForumAliasType.php b/src/Plugin/pathauto/AliasType/ForumAliasType.php index 2c62f9e..bbb1833 100644 --- a/src/Plugin/pathauto/AliasType/ForumAliasType.php +++ b/src/Plugin/pathauto/AliasType/ForumAliasType.php @@ -8,7 +8,6 @@ namespace Drupal\pathauto\Plugin\pathauto\AliasType; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\pathauto\AliasTypeBatchUpdateInterface; /** * A pathauto alias type plugin for forum terms. @@ -22,12 +21,6 @@ */ class ForumAliasType extends EntityAliasTypeBase implements ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return array('default' => array('/[term:vocabulary]/[term:name]')) + parent::defaultConfiguration(); - } /** * {@inheritdoc} diff --git a/src/Wizard/PatternWizard.php b/src/Wizard/PatternWizard.php deleted file mode 100644 index aa75654..0000000 --- a/src/Wizard/PatternWizard.php +++ /dev/null @@ -1,73 +0,0 @@ -t('Pattern'); - } - - /** - * {@inheritdoc} - */ - public function getMachineLabel() { - return $this->t('Identifier'); - } - - /** - * {@inheritdoc} - */ - public function getEntityType() { - return 'pathauto_pattern'; - } - - /** - * {@inheritdoc} - */ - public function exists() { - return 'Drupal\pathauto\Entity\PathautoPattern::load'; - } - - /** - * {@inheritdoc} - */ - public function getOperations($cached_values) { - return [ - 'general' => [ - 'title' => $this->t('General information'), - 'form' => PathautoPatternForm::class, - ], - 'contexts' => [ - 'title' => $this->t('Add contexts'), - 'form' => AddContext::class, - ], - 'selection_criteria' => [ - 'title' => $this->t('Selection criteria'), - 'form' => SelectionCriteriaForm::class, - ], - 'pattern' => [ - 'title' => $this->t('Configure pattern'), - 'form' => ConfigurePatternForm::class, - ], - ]; - } - -} diff --git a/src/Wizard/PatternWizardAdd.php b/src/Wizard/PatternWizardAdd.php deleted file mode 100644 index d7090bd..0000000 --- a/src/Wizard/PatternWizardAdd.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Sat, 5 Dec 2015 15:27:28 +0100 Subject: [PATCH 134/169] Avoid showing bundle option for entity types that have none --- src/Form/PatternEditForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/PatternEditForm.php b/src/Form/PatternEditForm.php index c0e457d..76c701f 100644 --- a/src/Form/PatternEditForm.php +++ b/src/Form/PatternEditForm.php @@ -143,7 +143,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { } } - if ($bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id())) { + if ($entity_type->hasKey('bundle') && $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id())) { $bundle_options = []; foreach ($bundles as $id => $info) { $bundle_options[$id] = $info['label']; From 4d1910084193d203777a8a9c5dd2258c454fbab5 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 16 Dec 2015 22:34:56 +0100 Subject: [PATCH 135/169] Fixed hasField() check --- pathauto.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.module b/pathauto.module index 8974c30..0169f48 100644 --- a/pathauto.module +++ b/pathauto.module @@ -75,7 +75,7 @@ function pathauto_entity_bundle_delete($entity_type, $bundle) { * Implements hook_entity_presave(). */ function pathauto_entity_presave($entity) { - if (!($entity instanceof ContentEntityInterface) || $entity->hasField('path')) { + if (!($entity instanceof ContentEntityInterface) || !$entity->hasField('path')) { return; } From c5a646d30f5727bad649945897dc7eb2574c5a93 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 13 Oct 2015 08:38:53 +0200 Subject: [PATCH 136/169] Issue #2114323 by alberto56, jarkkotuunanen, ollii, frakke: Switched using taxonomy_get_tree() to taxonomy_get_children() which fixes field tokens not generated for any child aliases, and improves performance. --- src/PathautoManager.php | 37 ++++++++++------------------ src/Tests/PathautoUnitTest.php | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/PathautoManager.php b/src/PathautoManager.php index 02c3604..4570656 100644 --- a/src/PathautoManager.php +++ b/src/PathautoManager.php @@ -277,7 +277,9 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } // Deal with taxonomy specific logic. + $data = array($type => $entity); if ($type == 'taxonomy_term') { + $data['term'] = $entity; $config_forum = $this->configFactory->get('forum.settings'); if ($entity->getVocabularyId() == $config_forum->get('vocabulary')) { @@ -286,13 +288,11 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } $result = $this->createAlias( - $type, $op, '/' . $entity->urlInfo()->getInternalPath(), array($type => $entity), $bundle, $options['language']); + $type, $op, '/' . $entity->urlInfo()->getInternalPath(), $data, $bundle, $options['language']); - if ($type == 'taxonomy_term' && empty($options['is_child'])) { - // For all children generate new aliases. - $options['is_child'] = TRUE; + if ($type == 'taxonomy_term') { unset($options['language']); - foreach ($this->getTermTree($entity->getVocabularyId(), $entity->id(), NULL, TRUE) as $subterm) { + foreach ($this->loadTermChildren($entity->id()) as $subterm) { $this->updateAlias($subterm, $op, $options); } } @@ -301,29 +301,16 @@ public function updateAlias(EntityInterface $entity, $op, array $options = array } /** - * Create a hierarchical representation of a vocabulary. + * Finds all children of a term ID. * - * @param int $vid - * The vocabulary ID to generate the tree for. - * @param int $parent - * The term ID under which to generate the tree. If 0, generate the tree - * for the entire vocabulary. - * @param int $max_depth - * The number of levels of the tree to return. Leave NULL to return all levels. - * @param bool $load_entities - * If TRUE, a full entity load will occur on the term objects. Otherwise they - * are partial objects queried directly from the {taxonomy_term_field_data} - * table to save execution time and memory consumption when listing large - * numbers of terms. Defaults to FALSE. + * @param int $tid + * Term ID to retrieve parents for. * - * @return array - * An array of all term objects in the tree. Each term object is extended - * to have "depth" and "parents" attributes in addition to its normal ones. - * Results are statically cached. Term objects will be partial or complete - * depending on the $load_entities parameter. + * @return \Drupal\taxonomy\TermInterface[] + * An array of term objects that are the children of the term $tid. */ - protected function getTermTree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) { - return \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities); + protected function loadTermChildren($tid) { + return \Drupal::entityManager()->getStorage('taxonomy_term')->loadChildren($tid); } } diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 2cd188f..8339301 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -8,11 +8,16 @@ namespace Drupal\pathauto\Tests; use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\Unicode; use Drupal\Core\Language\Language; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; use Drupal\node\Entity\NodeType; use Drupal\pathauto\PathautoManagerInterface; use Drupal\pathauto\PathautoState; use Drupal\simpletest\KernelTestBase; +use Drupal\taxonomy\Entity\Term; +use Drupal\taxonomy\Entity\Vocabulary; /** * Unit tests for Pathauto functions. @@ -290,6 +295,45 @@ public function testPathTokens() { $this->assertEntityAlias($term2, '/My Crazy/Alias/child-term'); } + + /** + * Test using fields for path structures. + */ + function testParentChildPathTokens() { + // First create a field which will be used to create the path. It must + // begin with a letter. + + $this->installEntitySchema('taxonomy_term'); + + Vocabulary::create(['vid' => 'tags'])->save(); + + $fieldname = 'a' . Unicode::strtolower($this->randomMachineName()); + $field_storage = FieldStorageConfig::create(['entity_type' => 'taxonomy_term', 'field_name' => $fieldname, 'type' => 'text']); + $field_storage->save(); + $field = FieldConfig::create(['field_storage' => $field_storage, 'bundle' => 'tags']); + $field->save(); + + // Make the path pattern of a field use the value of this field appended + // to the parent taxonomy term's pattern if there is one. + $config = $this->config('pathauto.pattern'); + $config->set('patterns.taxonomy_term.default', '[term:parents:join-path]/[term:' . $fieldname . ']'); + $config->save(); + + // Start by creating a parent term. + $parent = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'name' => $this->randomMachineName()]); + $parent->save(); + + // Create the child term. + $child = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'parent' => $parent, 'name' => $this->randomMachineName()]); + $child->save(); + $this->assertEntityAlias($child, Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); + + // Re-saving the parent term should not modify the child term's alias. + $parent->save(); + $this->assertEntityAlias($child, Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); + } + + public function testEntityBundleDeleting() { $config = $this->config('pathauto.pattern'); From bb274adefd3e479ecba56d85b2e25212a8b4ce72 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sat, 28 Nov 2015 13:43:22 +0100 Subject: [PATCH 137/169] Fix test, add missing display configuration. --- src/Tests/PathautoUnitTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 8339301..100b0fc 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -295,7 +295,6 @@ public function testPathTokens() { $this->assertEntityAlias($term2, '/My Crazy/Alias/child-term'); } - /** * Test using fields for path structures. */ @@ -308,15 +307,19 @@ function testParentChildPathTokens() { Vocabulary::create(['vid' => 'tags'])->save(); $fieldname = 'a' . Unicode::strtolower($this->randomMachineName()); - $field_storage = FieldStorageConfig::create(['entity_type' => 'taxonomy_term', 'field_name' => $fieldname, 'type' => 'text']); + $field_storage = FieldStorageConfig::create(['entity_type' => 'taxonomy_term', 'field_name' => $fieldname, 'type' => 'string']); $field_storage->save(); $field = FieldConfig::create(['field_storage' => $field_storage, 'bundle' => 'tags']); $field->save(); + $display = entity_get_display('taxonomy_term', 'tags', 'default'); + $display->setComponent($fieldname, ['type' => 'string']); + $display->save(); + // Make the path pattern of a field use the value of this field appended // to the parent taxonomy term's pattern if there is one. $config = $this->config('pathauto.pattern'); - $config->set('patterns.taxonomy_term.default', '[term:parents:join-path]/[term:' . $fieldname . ']'); + $config->set('patterns.taxonomy_term.default', '/[term:parents:join-path]/[term:' . $fieldname . ']'); $config->save(); // Start by creating a parent term. @@ -326,14 +329,13 @@ function testParentChildPathTokens() { // Create the child term. $child = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'parent' => $parent, 'name' => $this->randomMachineName()]); $child->save(); - $this->assertEntityAlias($child, Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); + $this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); // Re-saving the parent term should not modify the child term's alias. $parent->save(); - $this->assertEntityAlias($child, Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); + $this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); } - public function testEntityBundleDeleting() { $config = $this->config('pathauto.pattern'); From 003760f1dd38a8a0cb36b83a552047af842dd470 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 16 Dec 2015 22:34:56 +0100 Subject: [PATCH 138/169] Fixed hasField() check --- pathauto.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.module b/pathauto.module index 17ac516..bc2e516 100644 --- a/pathauto.module +++ b/pathauto.module @@ -74,7 +74,7 @@ function pathauto_entity_bundle_delete($entity_type, $bundle) { * Implements hook_entity_presave(). */ function pathauto_entity_presave($entity) { - if (!($entity instanceof ContentEntityInterface) || $entity->hasField('path')) { + if (!($entity instanceof ContentEntityInterface) || !$entity->hasField('path')) { return; } From 6422007c9a85a96be52342f6a937c1398b12743c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Wed, 16 Dec 2015 23:57:49 +0100 Subject: [PATCH 139/169] Remove setConfiguration() call from alias type --- src/Entity/PathautoPattern.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index f73402c..6e50c7d 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -218,7 +218,6 @@ public function getPattern() { */ public function setPattern($pattern) { $this->pattern = $pattern; - $this->getAliasType()->setConfiguration(['default' => $pattern]); return $this; } From c0311176f023887a5659d393bb59c5e2ee960630 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 18 Dec 2015 00:08:22 +0100 Subject: [PATCH 140/169] Avoid exception if an entity is not saved yet --- src/PathautoState.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PathautoState.php b/src/PathautoState.php index c591a17..7dcb184 100644 --- a/src/PathautoState.php +++ b/src/PathautoState.php @@ -40,6 +40,12 @@ class PathautoState extends TypedData { public function getValue() { if ($this->value === NULL) { $entity = $this->parent->getEntity(); + + // @todo: Investigate why this happens. + if ($entity->isNew()) { + $this->value = static::CREATE; + } + // If no value has been set or loaded yet, try to load a value if this // entity has already been saved. $this->value = \Drupal::keyValue($this->getCollection()) From e58a817db70f2f8a224b8beb8eb414dcd988224a Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 18 Dec 2015 00:18:00 +0100 Subject: [PATCH 141/169] Add missing return for PathautoState fix --- src/PathautoState.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PathautoState.php b/src/PathautoState.php index 7dcb184..e60ec6c 100644 --- a/src/PathautoState.php +++ b/src/PathautoState.php @@ -44,6 +44,7 @@ public function getValue() { // @todo: Investigate why this happens. if ($entity->isNew()) { $this->value = static::CREATE; + return $this->value; } // If no value has been set or loaded yet, try to load a value if this From 99111ea7eb65102c80b9893b818c66be97d05ae3 Mon Sep 17 00:00:00 2001 From: Juampy Date: Fri, 18 Dec 2015 13:46:00 +0100 Subject: [PATCH 142/169] Fix form redirection after deleting aliases The Bulk Delete form was redirecting to the Bulk Update form instead of back to Bulk Delete. --- src/Form/PathautoAdminDelete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/PathautoAdminDelete.php b/src/Form/PathautoAdminDelete.php index 524ad79..77003d0 100644 --- a/src/Form/PathautoAdminDelete.php +++ b/src/Form/PathautoAdminDelete.php @@ -111,7 +111,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { ->execute(); drupal_set_message(t('All of your %label path aliases have been deleted.', array('%label' => $alias_type->getLabel()))); } - $form_state->setRedirect('pathauto.bulk.update.form'); + $form_state->setRedirect('pathauto.admin.delete'); } } From 4c841b80e63df2d046906460e443aca435df311c Mon Sep 17 00:00:00 2001 From: Juampy Date: Sat, 19 Dec 2015 19:21:19 +0100 Subject: [PATCH 143/169] Fix typo in docblock --- src/Form/PathautoSettingsForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 2ca043b..0b56d3d 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\pathauto\Form\MaillogSettingsForm. + * Contains \Drupal\pathauto\Form\PathautoSettingsForm. */ namespace Drupal\pathauto\Form; From 25f18aed4020188f0966620f7dfde9f6e04464c0 Mon Sep 17 00:00:00 2001 From: hw Date: Thu, 24 Dec 2015 17:15:21 +0530 Subject: [PATCH 144/169] Use the appropriate theme call to render link to show token tree The dialog option for token browser is not used and is going to be removed in https://www.drupal.org/node/2640138. This commit changes the usages to the correct and more appropriate theme function. --- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index a5e0188..2d85bc2 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -168,9 +168,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta // Show the token help relevant to this pattern type. $form['token_help'] = array( - '#theme' => 'token_tree', + '#theme' => 'token_tree_link', '#token_types' => $this->getTokenTypes(), - '#dialog' => TRUE, ); return $form; } From 9d71dc78375dd89abb592d119b483929be90e3d1 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Thu, 24 Dec 2015 16:09:36 +0100 Subject: [PATCH 145/169] Fixed update function writes incorrecty value back to field storage definitions --- pathauto.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathauto.install b/pathauto.install index ee5a667..39e899d 100644 --- a/pathauto.install +++ b/pathauto.install @@ -36,7 +36,7 @@ function pathauto_update_8001() { $options['type'] = 'path'; $path_definition->setDisplayOptions('form', $options); // Save the new value. - $collection->set($key, $path_definition); + $collection->set($key, $definitions); } } From 29327b5c27437514faf99a5f1e4106ee50899563 Mon Sep 17 00:00:00 2001 From: Juampy Date: Fri, 25 Dec 2015 14:22:53 +0100 Subject: [PATCH 146/169] Test that the mass delete form redirects correctly --- src/Tests/PathautoMassDeleteTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tests/PathautoMassDeleteTest.php b/src/Tests/PathautoMassDeleteTest.php index b661f79..89cdec6 100644 --- a/src/Tests/PathautoMassDeleteTest.php +++ b/src/Tests/PathautoMassDeleteTest.php @@ -84,6 +84,7 @@ function testDeleteAll() { ); $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!')); $this->assertText(t('All of your path aliases have been deleted.')); + $this->assertUrl(\Drupal::url('pathauto.admin.delete')); // Make sure that all of them are actually deleted. $aliases = db_select('url_alias', 'ua')->fields('ua', array())->execute()->fetchAll(); From 8750f077fca6b5779e0acd8135f2617025e62a07 Mon Sep 17 00:00:00 2001 From: Juampy Date: Fri, 25 Dec 2015 16:13:16 +0100 Subject: [PATCH 147/169] Fix PathautoUiTest test Now that patterns are config entities, the UI tests were failing. On top of that, the Add form has an Ajax interaction to select the pattern type, which is covered too. --- src/Tests/PathautoUiTest.php | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Tests/PathautoUiTest.php b/src/Tests/PathautoUiTest.php index 1415aa9..b11d47d 100644 --- a/src/Tests/PathautoUiTest.php +++ b/src/Tests/PathautoUiTest.php @@ -84,20 +84,26 @@ function testSettingsValidation() { } function testPatternsValidation() { - $edit = array(); - $this->drupalGet('admin/config/search/path/patterns'); - $edit['node[default]'] = '[node:title]/[user:name]/[term:name]'; - $edit['node[bundles][page][default]'] = 'page'; - $this->drupalPostForm('admin/config/search/path/patterns', $edit, 'Save configuration'); - $this->assertText('The Default path pattern (applies to all content types with blank patterns below) is using the following invalid tokens: [user:name], [term:name].'); - $this->assertText('The Pattern for all Basic page paths cannot contain fewer than one token.'); + // Try to save an invalid pattern. + $this->drupalGet('admin/config/search/path/patterns/add'); + $edit = array( + 'type' => 'canonical_entities:node', + ); + $this->drupalPostAjaxForm(NULL, $edit, 'type'); + $edit += array( + 'pattern' => '[node:title]/[user:name]/[term:name]', + 'bundles[page]' => TRUE, + 'label' => 'Page pattern', + 'id' => 'page_pattern', + ); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('The Path pattern is using the following invalid tokens: [user:name], [term:name].'); $this->assertNoText('The configuration options have been saved.'); - $edit['node[default]'] = '[node:title]'; - $edit['node[bundles][page][default]'] = 'page/[node:title]'; - $edit['node[bundles][article][default]'] = ''; - $this->drupalPostForm('admin/config/search/path/patterns', $edit, 'Save configuration'); - $this->assertText('The configuration options have been saved.'); + // Fix the pattern, then check that it gets saved successfully. + $edit['pattern'] = '[node:title]'; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Pattern Page pattern saved.'); } } From 387eddb205e9c699a784b2ed48167613fa20db8f Mon Sep 17 00:00:00 2001 From: Juampy Date: Fri, 25 Dec 2015 19:36:02 +0100 Subject: [PATCH 148/169] [#79] Update README.md * Remove obsolete references. * Adjust functions that changed. --- README.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index a65d0f1..4c282f5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ #Pathauto [![Build Status](https://travis-ci.org/md-systems/pathauto.svg?branch=8.x-1.x)](https://travis-ci.org/md-systems/pathauto) -Please read this file and also the INSTALL.txt. -They contain answers to many common questions. -If you are developing for this module, the API.txt may be interesting. -If you are upgrading, check the CHANGELOG.txt for major changes. +If you are developing for this module, have a look at pathauto.api.php. ##Description @@ -27,10 +24,6 @@ automatically using keywords based directly on the page content in the URL, relevant search engine hits for your page can be significantly enhanced. -##Installation AND Upgrades - -See the INSTALL.txt file. - ##Notices Pathauto just adds URL aliases to content, users, and taxonomy terms. @@ -52,17 +45,15 @@ Global Redirect modules, which allow you to set forwarding either per item or across the site to your aliased URLs. URLs (not) Getting Replaced With Aliases: -Please bear in mind that only URLs passed through Drupal's l() or url() -functions will be replaced with their aliases during page output. If a module -or your template contains hardcoded links, such as 'href="node/$node->nid"' -those won't get replaced with their corresponding aliases. Use the -Drupal API instead: - -* 'href="'. url("node/$node->nid") .'"' or -* l("Your link title", "node/$node->nid") - -See http://api.drupal.org/api/HEAD/function/url and -http://api.drupal.org/api/HEAD/function/l for more information. +Please bear in mind that only URLs passed through Drupal's Link::fromTextAndUrl +or Url::fromRoute() will be replaced with their aliases during page output. If +a module or your template contains hardcoded links, such as +'href="node/$node->nid"', those won't get replaced with their corresponding +aliases. + +See https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Link.php/function/Link%3A%3AfromTextAndUrl/8 +and https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Url.php/function/Url%3A%3AfromRoute/8 +for more information. ## Disabling Pathauto for a specific content type (or taxonomy) From 615a633c6bc8a2fc39a2560ddb2007795f0931d0 Mon Sep 17 00:00:00 2001 From: Juampy NR Date: Sat, 26 Dec 2015 15:10:01 +0100 Subject: [PATCH 149/169] Adjust wording for link APIs at README.md --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4c282f5..56d449c 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ Implementations are provided for core entity types: content, taxonomy terms, and users (including blogs and forum pages). Pathauto also provides a way to delete large numbers of aliases. This feature -is available at Administer > Configuration > Search and metadata > URL aliases -> Delete aliases. +is available at Administer > Configuration > Search and metadata > URL aliases > +Delete aliases. ##Benefits @@ -44,17 +44,13 @@ For external links, you might want to consider the Path Redirect or Global Redirect modules, which allow you to set forwarding either per item or across the site to your aliased URLs. -URLs (not) Getting Replaced With Aliases: -Please bear in mind that only URLs passed through Drupal's Link::fromTextAndUrl -or Url::fromRoute() will be replaced with their aliases during page output. If +###URLs (not) Getting Replaced With Aliases: +Please bear in mind that only URLs passed through Drupal's Drupal's URL and +Link APIs will be replaced with their aliases during page output. If a module or your template contains hardcoded links, such as 'href="node/$node->nid"', those won't get replaced with their corresponding aliases. -See https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Link.php/function/Link%3A%3AfromTextAndUrl/8 -and https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Url.php/function/Url%3A%3AfromRoute/8 -for more information. - ## Disabling Pathauto for a specific content type (or taxonomy) When the pattern for a content type is left blank, the default pattern will be From 8bba1aa51939a7a915d7b4a990dc8a5660ebaf9b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 27 Dec 2015 14:30:24 +0100 Subject: [PATCH 150/169] Use the appropriate theme call to render link to show token tree --- src/Form/PatternEditForm.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Form/PatternEditForm.php b/src/Form/PatternEditForm.php index 76c701f..3c55207 100644 --- a/src/Form/PatternEditForm.php +++ b/src/Form/PatternEditForm.php @@ -124,9 +124,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { // Show the token help relevant to this pattern type. $form['pattern_container']['token_help'] = array( - '#theme' => 'token_tree', + '#theme' => 'token_tree_link', '#token_types' => $alias_type->getTokenTypes(), - '#dialog' => TRUE, ); // Expose bundle and language conditions. From ce8869aa3a9f02e470e91d2b7bb9f906b448ae7c Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Sun, 27 Dec 2015 14:34:32 +0100 Subject: [PATCH 151/169] Set correct weight for page pattern --- src/Tests/PathautoUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index 5fe288d..d59b29c 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -108,7 +108,7 @@ public function testPatternLoadByEntity() { $pattern->addContext('node:langcode:language', $new_context); $pattern->save(); - $pattern = $this->createPattern('node', '/[node:title]'); + $pattern = $this->createPattern('node', '/[node:title]', -1); $this->addBundleCondition($pattern, 'node', 'page'); $pattern->save(); From 9e242e7d106eaf2d0d81373f1c6ae2928fafbfe1 Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 28 Dec 2015 16:53:01 +0100 Subject: [PATCH 152/169] Don't expect Pathauto to delete patterns on bundle deletion The Drupal 7 version of Pathauto had logic to evaluate a bundle being deleted and see if there were matching patterns that had to be removed as well. This is something that we don't see a real need for. Instead, we will let the site administrator to clean up patterns manually after removing a bundle. --- pathauto.module | 10 ---------- src/Tests/PathautoUnitTest.php | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/pathauto.module b/pathauto.module index 0169f48..cc1d24e 100644 --- a/pathauto.module +++ b/pathauto.module @@ -61,16 +61,6 @@ function pathauto_help($route_name, RouteMatchInterface $route_match) { } } -/** - * Implements hook_entity_bundle_delete(). - */ -function pathauto_entity_bundle_delete($entity_type, $bundle) { - // @todo Update or remove this. - // $config = \Drupal::configFactory()->getEditable('pathauto.pattern'); - // $config->clear('patterns.' . $entity_type . '.bundles.' . $bundle); - // $config->save(); -} - /** * Implements hook_entity_presave(). */ diff --git a/src/Tests/PathautoUnitTest.php b/src/Tests/PathautoUnitTest.php index d59b29c..1a89d5e 100644 --- a/src/Tests/PathautoUnitTest.php +++ b/src/Tests/PathautoUnitTest.php @@ -378,25 +378,20 @@ function testParentChildPathTokens() { $this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value)); } - public function testEntityBundleDeleting() { + /** + * Tests aliases on taxonomy terms. + */ + public function testTaxonomyPattern() { // Create a vocabulary and test that it's pattern variable works. $vocab = $this->addVocabulary(array('vid' => 'name')); $this->createPattern('taxonomy_term', 'base'); $pattern = $this->createPattern('taxonomy_term', 'bundle', -1); $this->addBundleCondition($pattern, 'taxonomy_term', 'name'); $pattern->save(); - - $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'bundle'); - - // Delete the vocabulary, which should cause its pattern variable to also - // be deleted. - $vocab->delete(); - $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'base'); } function testNoExistingPathAliases() { - $this->config('pathauto.settings') ->set('punctuation.period', PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING) ->save(); @@ -449,7 +444,7 @@ function testProgrammaticEntityCreation() { } /** - * Tests word safe alias truncating truncating. + * Tests word safe alias truncating. */ function testPathAliasUniquifyWordsafe() { $this->config('pathauto.settings') @@ -466,6 +461,15 @@ function testPathAliasUniquifyWordsafe() { $this->assertEntityAlias($node_2, '/content/thequick-0'); } + /** + * Creates a node programmatically. + * + * @param array $settings + * The array of values for the node. + * + * @return \Drupal\node\Entity\Node + * The created node. + */ protected function drupalCreateNode(array $settings = array()) { // Populate defaults array. $settings += array( From 2297ab68ee62aa2ab905bcc488cd69e5b3ce4a1d Mon Sep 17 00:00:00 2001 From: Juampy Date: Tue, 29 Dec 2015 14:40:59 +0100 Subject: [PATCH 153/169] [#10] Port patterns from config objects to config entities --- pathauto.install | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/pathauto.install b/pathauto.install index 39e899d..2b02266 100644 --- a/pathauto.install +++ b/pathauto.install @@ -8,6 +8,7 @@ */ use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Utility\UpdateException; /** * Implements hook_install(). @@ -51,3 +52,101 @@ function pathauto_update_8001() { } } } + +/** + * Converts patterns from configuration objects to configuration entities. + */ +function pathauto_update_8100(&$sandbox) { + $messages = array(); + $delete_config = TRUE; + + if (!\Drupal::service('module_handler')->moduleExists('ctools')) { + throw new UpdateException('Please, install Chaos tools suite (https://www.drupal.org/project/ctools) before running this databsae update.'); + } + + $entity_manager = \Drupal::service('entity.manager'); + $entity_info = $entity_manager->getDefinitions(); + + // 1. Load all patterns. + $config = \Drupal::service('config.factory')->getEditable('pathauto.pattern'); + $patterns = $config->get('patterns'); + + // 2. Create a configuration entity per pattern. + foreach ($patterns as $entity_type => $pattern_config) { + if (!array_key_exists($entity_type, $entity_info)) { + // We found an entity type which we don't know how to process. Report it. + $messages[] = t('Entity of type @type was not processed.', array('@type' => $entity_type)); + $delete_config = FALSE; + continue; + } + $entity_label = (string) $entity_info[$entity_type]->get('label'); + // Create and save the default pattern for this entity. + if (isset($pattern_config['default'])) { + // Check if the administrator created this pattern before running updates. + // If there is a pattern, skip to the next one. + $entity = entity_load('pathauto_pattern', $entity_type); + if ($entity) { + continue; + } + // There is no default pattern. Create and save. + $entity = entity_create('pathauto_pattern', [ + 'id' => $entity_type, + 'label' => $entity_label, + 'type' => 'canonical_entities:' . $entity_type, + 'pattern' => $pattern_config['default'], + 'weight' => 0, + ]); + $entity->save(); + } + // Loop over bundles and create patterns if they override the default + // pattern. + if (isset($pattern_config['bundles'])) { + foreach ($pattern_config['bundles'] as $bundle => $bundle_config) { + if (!empty($bundle_config['default'])) { + $id = $entity_type . '_' . $bundle; + // Check if the administrator created this pattern before running updates. + // If there is a pattern, skip to the next one. + $entity = entity_load('pathauto_pattern', $id); + if ($entity) { + continue; + } + + // Figure out the label of this pattern. + $bundle_info = $entity_manager->getBundleInfo($entity_type); + $bundle_label = $bundle_info[$bundle]['label']; + $label = $entity_label . ' ' . $bundle_label; + + // Create and save the pattern for this entity bundle. + $entity = entity_create('pathauto_pattern', [ + 'id' => $id, + 'label' => $label, + 'type' => 'canonical_entities:' . $entity_type, + 'pattern' => $bundle_config['default'], + 'weight' => -5, + ]); + + // Add the bundle condition. + $entity->addSelectionCondition([ + 'id' => 'entity_bundle:' . $entity_type, + 'bundles' => array($bundle), + 'negate' => FALSE, + 'context_mapping' => [ + $entity_type => $entity_type, + ], + ]); + + $entity->save(); + } + } + } + } + + // 3. Delete the old configuration object that stores patterns only if all + // configuration objects were processed. + if ($delete_config) { + $config->delete(); + } + if (!empty($messages)) { + return implode('
', $messages); + } +} From 44c3b2f5bc3599b86953976dbc48994d2a2e3af5 Mon Sep 17 00:00:00 2001 From: Juampy Date: Wed, 30 Dec 2015 13:25:17 +0100 Subject: [PATCH 154/169] [#10] Add support to port language-dependant patterns --- pathauto.install | 126 ++++++++++++++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/pathauto.install b/pathauto.install index 2b02266..4683860 100644 --- a/pathauto.install +++ b/pathauto.install @@ -57,14 +57,14 @@ function pathauto_update_8001() { * Converts patterns from configuration objects to configuration entities. */ function pathauto_update_8100(&$sandbox) { - $messages = array(); - $delete_config = TRUE; - if (!\Drupal::service('module_handler')->moduleExists('ctools')) { - throw new UpdateException('Please, install Chaos tools suite (https://www.drupal.org/project/ctools) before running this databsae update.'); + throw new UpdateException('Install Chaos tools suite (https://www.drupal.org/project/ctools) before running this database update.'); } + $messages = array(); $entity_manager = \Drupal::service('entity.manager'); + $entity_type_manager = \Drupal::service('entity_type.manager'); + $language_manager = \Drupal::service('language_manager'); $entity_info = $entity_manager->getDefinitions(); // 1. Load all patterns. @@ -72,56 +72,46 @@ function pathauto_update_8100(&$sandbox) { $patterns = $config->get('patterns'); // 2. Create a configuration entity per pattern. - foreach ($patterns as $entity_type => $pattern_config) { + foreach ($patterns as $entity_type => $entity_patterns) { if (!array_key_exists($entity_type, $entity_info)) { - // We found an entity type which we don't know how to process. Report it. - $messages[] = t('Entity of type @type was not processed.', array('@type' => $entity_type)); - $delete_config = FALSE; + // We found an unknown entity type. Report it. + $messages[] = t('Entity of type @type was not processed. It defines the following patterns: @patterns', array( + '@type' => $entity_type, + '@patterns' => print_r($entity_patterns, TRUE), + )); continue; } - $entity_label = (string) $entity_info[$entity_type]->get('label'); - // Create and save the default pattern for this entity. - if (isset($pattern_config['default'])) { - // Check if the administrator created this pattern before running updates. - // If there is a pattern, skip to the next one. - $entity = entity_load('pathauto_pattern', $entity_type); - if ($entity) { - continue; - } - // There is no default pattern. Create and save. + $entity_label = $entity_info[$entity_type]->getLabel(); + + if (isset($entity_patterns['default'])) { + // This is a pattern for an entity type, such as "node". $entity = entity_create('pathauto_pattern', [ 'id' => $entity_type, 'label' => $entity_label, 'type' => 'canonical_entities:' . $entity_type, - 'pattern' => $pattern_config['default'], + 'pattern' => $entity_patterns['default'], 'weight' => 0, ]); $entity->save(); } - // Loop over bundles and create patterns if they override the default - // pattern. - if (isset($pattern_config['bundles'])) { - foreach ($pattern_config['bundles'] as $bundle => $bundle_config) { - if (!empty($bundle_config['default'])) { - $id = $entity_type . '_' . $bundle; - // Check if the administrator created this pattern before running updates. - // If there is a pattern, skip to the next one. - $entity = entity_load('pathauto_pattern', $id); - if ($entity) { - continue; - } - // Figure out the label of this pattern. - $bundle_info = $entity_manager->getBundleInfo($entity_type); - $bundle_label = $bundle_info[$bundle]['label']; - $label = $entity_label . ' ' . $bundle_label; + // Loop over bundles and create patterns if they have a value. + // Bundle keys may have a language suffix for language-dependant patterns. + if (isset($entity_patterns['bundles'])) { + $bundle_info = $entity_manager->getBundleInfo($entity_type); + foreach ($entity_patterns['bundles'] as $bundle => $bundle_patterns) { + if (empty($bundle_patterns['default'])) { + // This bundle does not define a pattern. Move on to the next one. + continue; + } - // Create and save the pattern for this entity bundle. + if (isset($bundle_info[$bundle])) { + // This is a pattern for a bundle, such as "node_article". $entity = entity_create('pathauto_pattern', [ - 'id' => $id, - 'label' => $label, + 'id' => $entity_type . '_' . $bundle, + 'label' => $entity_label . ' ' . $bundle_info[$bundle]['label'], 'type' => 'canonical_entities:' . $entity_type, - 'pattern' => $bundle_config['default'], + 'pattern' => $bundle_patterns['default'], 'weight' => -5, ]); @@ -130,9 +120,56 @@ function pathauto_update_8100(&$sandbox) { 'id' => 'entity_bundle:' . $entity_type, 'bundles' => array($bundle), 'negate' => FALSE, + 'context_mapping' => [ $entity_type => $entity_type ], + ]); + + $entity->save(); + } + else { + // This is either a language dependent pattern such as "article_es" or + // an unknown bundle or langcode. Let's figure it out. + $matches = NULL; + $langcode = NULL; + preg_match('/^(.*)_([a-z-]*)$/', $bundle, $matches); + if (count($matches) == 3) { + list(, $extracted_bundle, $langcode) = $matches; + $language = $language_manager->getLanguage($langcode); + } + // Validate bundle, langcode and language. + if (!isset($bundle_info[$extracted_bundle]) || ($langcode == NULL) || ($language == NULL)) { + $messages[] = t('Unrecognized entity bundle @entity:@bundle was not processed. It defines the following patterns: @patterns', array( + '@entity' => $entity_type, + '@bundle' => $bundle, + '@patterns' => print_r($entity_patterns, TRUE), + )); + continue; + } + + // This is a pattern for a bundle and a language, such as "node_article_es". + $entity = entity_create('pathauto_pattern', [ + 'id' => $entity_type . '_' . $extracted_bundle . '_' . $langcode, + 'label' => $entity_label . ' ' . $bundle_info[$extracted_bundle]['label'] . ' ' . $language->getName(), + 'type' => 'canonical_entities:' . $entity_type, + 'pattern' => $bundle_patterns['default'], + 'weight' => -10, + ]); + + // Add the bundle condition. + $entity->addSelectionCondition([ + 'id' => 'entity_bundle:' . $entity_type, + 'bundles' => array($extracted_bundle), + 'negate' => FALSE, + 'context_mapping' => [ $entity_type => $entity_type ], + ]); + + // Add the language condition. + $entity->addSelectionCondition([ + 'id' => 'language', + 'langcodes' => [ $langcode => $langcode ], + 'negate' => FALSE, 'context_mapping' => [ - $entity_type => $entity_type, - ], + 'language' => $entity_type . ':' . $entity_type_manager->getDefinition($entity_type)->getKey('langcode') . ':language', + ] ]); $entity->save(); @@ -142,10 +179,9 @@ function pathauto_update_8100(&$sandbox) { } // 3. Delete the old configuration object that stores patterns only if all - // configuration objects were processed. - if ($delete_config) { - $config->delete(); - } + $config->delete(); + + // 4. Print out messages. if (!empty($messages)) { return implode('
', $messages); } From 4295382b507514663b44dd253412bcdf7bbbb792 Mon Sep 17 00:00:00 2001 From: Juampy Date: Thu, 31 Dec 2015 12:11:18 +0100 Subject: [PATCH 155/169] [#10] Add language context to patterns Plus: * Small improvements suggested by Berdir. * Make sure that the langcode is a valid machine-name. For example, Chinese traditional has a langcode zh-hant, which is not valid for a machine name. --- pathauto.install | 41 ++++++++++++++++++++++-------------- src/Form/PatternEditForm.php | 8 ++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/pathauto.install b/pathauto.install index 4683860..23babee 100644 --- a/pathauto.install +++ b/pathauto.install @@ -9,6 +9,9 @@ use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Utility\UpdateException; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\pathauto\Entity\PathautoPattern; /** * Implements hook_install(). @@ -65,7 +68,7 @@ function pathauto_update_8100(&$sandbox) { $entity_manager = \Drupal::service('entity.manager'); $entity_type_manager = \Drupal::service('entity_type.manager'); $language_manager = \Drupal::service('language_manager'); - $entity_info = $entity_manager->getDefinitions(); + $entity_types = $entity_manager->getDefinitions(); // 1. Load all patterns. $config = \Drupal::service('config.factory')->getEditable('pathauto.pattern'); @@ -73,7 +76,7 @@ function pathauto_update_8100(&$sandbox) { // 2. Create a configuration entity per pattern. foreach ($patterns as $entity_type => $entity_patterns) { - if (!array_key_exists($entity_type, $entity_info)) { + if (!array_key_exists($entity_type, $entity_types)) { // We found an unknown entity type. Report it. $messages[] = t('Entity of type @type was not processed. It defines the following patterns: @patterns', array( '@type' => $entity_type, @@ -81,18 +84,18 @@ function pathauto_update_8100(&$sandbox) { )); continue; } - $entity_label = $entity_info[$entity_type]->getLabel(); + $entity_label = $entity_types[$entity_type]->getLabel(); if (isset($entity_patterns['default'])) { // This is a pattern for an entity type, such as "node". - $entity = entity_create('pathauto_pattern', [ + $pattern = PathautoPattern::create([ 'id' => $entity_type, 'label' => $entity_label, 'type' => 'canonical_entities:' . $entity_type, 'pattern' => $entity_patterns['default'], 'weight' => 0, ]); - $entity->save(); + $pattern->save(); } // Loop over bundles and create patterns if they have a value. @@ -107,7 +110,7 @@ function pathauto_update_8100(&$sandbox) { if (isset($bundle_info[$bundle])) { // This is a pattern for a bundle, such as "node_article". - $entity = entity_create('pathauto_pattern', [ + $pattern = PathautoPattern::create([ 'id' => $entity_type . '_' . $bundle, 'label' => $entity_label . ' ' . $bundle_info[$bundle]['label'], 'type' => 'canonical_entities:' . $entity_type, @@ -116,14 +119,14 @@ function pathauto_update_8100(&$sandbox) { ]); // Add the bundle condition. - $entity->addSelectionCondition([ + $pattern->addSelectionCondition([ 'id' => 'entity_bundle:' . $entity_type, 'bundles' => array($bundle), 'negate' => FALSE, 'context_mapping' => [ $entity_type => $entity_type ], ]); - $entity->save(); + $pattern->save(); } else { // This is either a language dependent pattern such as "article_es" or @@ -146,8 +149,8 @@ function pathauto_update_8100(&$sandbox) { } // This is a pattern for a bundle and a language, such as "node_article_es". - $entity = entity_create('pathauto_pattern', [ - 'id' => $entity_type . '_' . $extracted_bundle . '_' . $langcode, + $pattern = PathautoPattern::create([ + 'id' => $entity_type . '_' . $extracted_bundle . '_' . str_replace('-', '_', $langcode), 'label' => $entity_label . ' ' . $bundle_info[$extracted_bundle]['label'] . ' ' . $language->getName(), 'type' => 'canonical_entities:' . $entity_type, 'pattern' => $bundle_patterns['default'], @@ -155,30 +158,36 @@ function pathauto_update_8100(&$sandbox) { ]); // Add the bundle condition. - $entity->addSelectionCondition([ + $pattern->addSelectionCondition([ 'id' => 'entity_bundle:' . $entity_type, - 'bundles' => array($extracted_bundle), + 'bundles' => array($extracted_bundle => $extracted_bundle), 'negate' => FALSE, 'context_mapping' => [ $entity_type => $entity_type ], ]); // Add the language condition. - $entity->addSelectionCondition([ + $language_mapping = $entity_type . ':' . $entity_type_manager->getDefinition($entity_type)->getKey('langcode') . ':language'; + $pattern->addSelectionCondition([ 'id' => 'language', 'langcodes' => [ $langcode => $langcode ], 'negate' => FALSE, 'context_mapping' => [ - 'language' => $entity_type . ':' . $entity_type_manager->getDefinition($entity_type)->getKey('langcode') . ':language', + 'language' => $language_mapping, ] ]); - $entity->save(); + // Add the context relationship for this language. + $new_definition = new ContextDefinition('language', 'Language'); + $new_context = new Context($new_definition); + $pattern->addContext($language_mapping, $new_context); + + $pattern->save(); } } } } - // 3. Delete the old configuration object that stores patterns only if all + // 3. Delete the old configuration object that stores patterns. $config->delete(); // 4. Print out messages. diff --git a/src/Form/PatternEditForm.php b/src/Form/PatternEditForm.php index 3c55207..02615fa 100644 --- a/src/Form/PatternEditForm.php +++ b/src/Form/PatternEditForm.php @@ -12,6 +12,8 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\pathauto\AliasTypeManager; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -231,16 +233,20 @@ public function buildEntity(array $form, FormStateInterface $form_state) { if ($languages = array_filter((array) $form_state->getValue('languages'))) { $default_weight -= 5; + $language_mapping = $entity_type . ':' . $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode') . ':language'; $entity->addSelectionCondition( [ 'id' => 'language', 'langcodes' => array_combine($languages, $languages), 'negate' => FALSE, 'context_mapping' => [ - 'language' => $entity_type . ':' . $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode') . ':language', + 'language' => $language_mapping, ] ] ); + $new_definition = new ContextDefinition('language', 'Language'); + $new_context = new Context($new_definition); + $entity->addContext($language_mapping, $new_context); } } From 8710697a2dc3e09eba78382459b93c08c0842e29 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 1 Jan 2016 09:17:19 +0100 Subject: [PATCH 156/169] Remove leftover pattern form --- src/Form/PathautoPatternForm.php | 82 -------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 src/Form/PathautoPatternForm.php diff --git a/src/Form/PathautoPatternForm.php b/src/Form/PathautoPatternForm.php deleted file mode 100644 index accbc64..0000000 --- a/src/Form/PathautoPatternForm.php +++ /dev/null @@ -1,82 +0,0 @@ -get('plugin.manager.alias_type')); - } - - /** - * @param \Drupal\pathauto\AliasTypeManager $manager - */ - function __construct(AliasTypeManager $manager) { - $this->manager = $manager; - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'pathauto_pattern_general_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $cached_values = $form_state->getTemporaryValue('wizard'); - /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ - $pathauto_pattern = $cached_values['pathauto_pattern']; - $options = []; - foreach ($this->manager->getDefinitions() as $plugin_id => $plugin_definition) { - $options[$plugin_id] = $plugin_definition['label']; - } - $form['type'] = [ - '#type' => 'select', - '#title' => $this->t('Pattern type'), - '#default_value' => $pathauto_pattern->getType(), - '#options' => $options, - '#required' => TRUE, - ]; - - return $form; - } - - public function submitForm(array &$form, FormStateInterface $form_state) { - $cached_values = $form_state->getTemporaryValue('wizard'); - /** @var $pathauto_pattern \Drupal\pathauto\PathautoPatternInterface */ - $pathauto_pattern = $cached_values['pathauto_pattern']; - $pathauto_pattern->set('label', $form_state->getValue('label')); - $pathauto_pattern->set('id', $form_state->getValue('id')); - $pathauto_pattern->set('type', $form_state->getValue('type')); - } - - -} From 3ad217bd91031dda73b0d504e90d5bd9829247c3 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 1 Jan 2016 09:17:35 +0100 Subject: [PATCH 157/169] Fix punctuation settings --- src/Form/PathautoSettingsForm.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Form/PathautoSettingsForm.php b/src/Form/PathautoSettingsForm.php index 0b56d3d..edfb7dd 100644 --- a/src/Form/PathautoSettingsForm.php +++ b/src/Form/PathautoSettingsForm.php @@ -159,16 +159,15 @@ public function buildForm(array $form, FormStateInterface $form_state) { $punctuation = \Drupal::service('pathauto.alias_cleaner')->getPunctuationCharacters(); foreach ($punctuation as $name => $details) { - if (!$config->get('punctuation.punctuation'. $name)) { - $details['default'] = PathautoGeneratorInterface::PUNCTUATION_REMOVE; + // Use the value from config if it exists. + if ($config->get('punctuation.' . $name) !== NULL) { + $details['default'] = $config->get('punctuation.' . $name) !== NULL; } else { - $details['default'] = $config->get('punctuation.punctuation'. $name); + // Otherwise use the correct default. + $details['default'] = $details['value'] == $config->get('separator') ? PathautoGeneratorInterface::PUNCTUATION_REPLACE : PathautoGeneratorInterface::PUNCTUATION_REMOVE; } - if ($details['value'] == $config->get('separator')) { - $details['default'] = PathautoGeneratorInterface::PUNCTUATION_REPLACE; - } - $form['punctuation']['punctuation' . $name] = array( + $form['punctuation'][$name] = array( '#type' => 'select', '#title' => $details['name'] . ' (' . SafeMarkup::checkPlain($details['value']) . ')', '#default_value' => $details['default'], From 5512cdc31b1ea21626f0dc964b5b2db3e224e9c8 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 1 Jan 2016 09:18:23 +0100 Subject: [PATCH 158/169] Improve documentation, fix plugin dependencies --- src/AliasTypeInterface.php | 3 ++- src/Entity/PathautoPattern.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/AliasTypeInterface.php b/src/AliasTypeInterface.php index 80d9319..c890a8e 100644 --- a/src/AliasTypeInterface.php +++ b/src/AliasTypeInterface.php @@ -42,10 +42,11 @@ public function getSourcePrefix(); /** * Determines if this plugin type can apply a given object. * - * @param $object + * @param object $object * The object used to determine if this plugin can apply. * * @return bool + * Whether this plugin applies to the given object. */ public function applies($object); diff --git a/src/Entity/PathautoPattern.php b/src/Entity/PathautoPattern.php index 6e50c7d..f7619e8 100644 --- a/src/Entity/PathautoPattern.php +++ b/src/Entity/PathautoPattern.php @@ -197,7 +197,7 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti public function calculateDependencies() { parent::calculateDependencies(); - // @todo get dependencies from the alias type plugin. + $this->calculatePluginDependencies($this->getAliasType()); foreach ($this->getSelectionConditions() as $instance) { $this->calculatePluginDependencies($instance); From 468a72febba5fe10976f875033f9b451f448324b Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Fri, 1 Jan 2016 09:44:09 +0100 Subject: [PATCH 159/169] Use token entity mapper to clean up token mapping code --- pathauto.services.yml | 2 +- src/PathautoGenerator.php | 20 ++++-- src/Plugin/Deriver/EntityAliasTypeDeriver.php | 65 +++++++++++++++++-- .../AliasType/EntityAliasTypeBase.php | 6 -- 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/pathauto.services.yml b/pathauto.services.yml index cf7821e..748de19 100644 --- a/pathauto.services.yml +++ b/pathauto.services.yml @@ -1,7 +1,7 @@ services: pathauto.generator: class: Drupal\pathauto\PathautoGenerator - arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation'] + arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation', '@token.entity_mapper'] pathauto.alias_cleaner: class: Drupal\pathauto\AliasCleaner arguments: ['@config.factory', '@pathauto.alias_storage_helper', '@language_manager', '@cache.discovery', '@transliteration', '@module_handler'] diff --git a/src/PathautoGenerator.php b/src/PathautoGenerator.php index 7adbf7c..f981610 100644 --- a/src/PathautoGenerator.php +++ b/src/PathautoGenerator.php @@ -16,6 +16,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\Utility\Token; +use Drupal\token\TokenEntityMapperInterface; /** * Provides methods for generating path aliases. @@ -87,6 +88,11 @@ class PathautoGenerator implements PathautoGeneratorInterface { */ protected $messenger; + /** + * @var \Drupal\token\TokenEntityMapperInterface + */ + protected $tokenEntityMapper; + /** * Creates a new Pathauto manager. * @@ -107,7 +113,7 @@ class PathautoGenerator implements PathautoGeneratorInterface { * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation service. */ - public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mappper) { $this->configFactory = $config_factory; $this->moduleHandler = $module_handler; $this->token = $token; @@ -116,6 +122,7 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle $this->aliasUniquifier = $alias_uniquifier; $this->messenger = $messenger; $this->stringTranslation = $string_translation; + $this->tokenEntityMapper = $token_entity_mappper; } /** @@ -135,11 +142,9 @@ public function createEntityAlias(EntityInterface $entity, $op) { // Build token data. $data = [ - $entity->getEntityTypeId() => $entity + $this->tokenEntityMapper->getTokenTypeForEntityType($entity->getEntityTypeId()) => $entity, ]; - if ($entity->getEntityTypeId() == 'taxonomy_term') { - $data['term'] = $entity; - } + debug(array_keys($data)); // Allow other modules to alter the pattern. $context = array( @@ -237,7 +242,7 @@ protected function getPatternByEntityType($entity_type_id) { if (!isset($this->patternsByEntityType[$entity_type_id])) { $ids = \Drupal::entityQuery('pathauto_pattern') ->condition('type', array_keys(\Drupal::service('plugin.manager.alias_type') - ->getPluginDefinitionByType($entity_type_id))) + ->getPluginDefinitionByType($this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id)))) ->sort('weight') ->execute(); @@ -300,6 +305,7 @@ public function updateEntityAlias(EntityInterface $entity, $op, array $options = } // Deal with taxonomy specific logic. + // @todo Update and test forum related code. if ($type == 'taxonomy_term') { $config_forum = $this->configFactory->get('forum.settings'); @@ -310,8 +316,8 @@ public function updateEntityAlias(EntityInterface $entity, $op, array $options = $result = $this->createEntityAlias($entity, $op); + // @todo Move this to a method on the pattern plugin. if ($type == 'taxonomy_term') { - unset($options['language']); foreach ($this->loadTermChildren($entity->id()) as $subterm) { $this->updateEntityAlias($subterm, $op, $options); } diff --git a/src/Plugin/Deriver/EntityAliasTypeDeriver.php b/src/Plugin/Deriver/EntityAliasTypeDeriver.php index 58a2abf..f846d48 100644 --- a/src/Plugin/Deriver/EntityAliasTypeDeriver.php +++ b/src/Plugin/Deriver/EntityAliasTypeDeriver.php @@ -6,23 +6,78 @@ namespace Drupal\pathauto\Plugin\Deriver; +use Drupal\Component\Plugin\Derivative\DeriverBase; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Plugin\Context\ContextDefinition; -use Drupal\ctools\Plugin\Deriver\EntityDeriverBase; +use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\token\TokenEntityMapperInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Deriver that exposes content entities as alias type plugins. */ -class EntityAliasTypeDeriver extends EntityDeriverBase { +class EntityAliasTypeDeriver extends DeriverBase implements ContainerDeriverInterface { + + use StringTranslationTrait; + + /** + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * @var \Drupal\Core\Entity\EntityFieldManagerInterface + */ + protected $entityFieldManager; + + /** + * @var \Drupal\token\TokenEntityMapperInterface + */ + protected $tokenEntityMapper; + + /** + * Constructs new EntityAliasTypeDeriver. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager + * The entity field manager. + * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation + * The string translation service. + * @apram \Drupal\Token\TokenEntityMapperInterface $token_entity_mapper + * The token entity mapper. + */ + public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mapper) { + $this->entityTypeManager = $entity_type_manager; + $this->entityFieldManager = $entity_field_manager; + $this->stringTranslation = $string_translation; + $this->tokenEntityMapper = $token_entity_mapper; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('entity_type.manager'), + $container->get('entity_field.manager'), + $container->get('string_translation'), + $container->get('token.entity_mapper') + ); + } /** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { - foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { // An entity type must have a canonical link template and support fields. if ($entity_type->hasLinkTemplate('canonical') && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class)) { - $base_fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id); + $base_fields = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id); if (!isset($base_fields['path'])) { // The entity type does not have a path field and is therefore not // supported. @@ -31,7 +86,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { } $this->derivatives[$entity_type_id] = $base_plugin_definition; $this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel(); - $this->derivatives[$entity_type_id]['types'] = [$entity_type_id]; + $this->derivatives[$entity_type_id]['types'] = [$this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id)]; $this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider(); $this->derivatives[$entity_type_id]['context'] = [ $entity_type_id => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()])) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 7868ad1..5c2c635 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -107,12 +107,6 @@ public function getLabel() { */ public function getTokenTypes() { $definition = $this->getPluginDefinition(); - // For some reason, we didn't unify token keys with entity types... - foreach ($definition['types'] as $key => $type) { - if ($type == 'taxonomy_term') { - $definition['types'][$key] = 'term'; - } - } return $definition['types']; } From c0802b2763509420c709a10a378d53664acd79d8 Mon Sep 17 00:00:00 2001 From: Juampy Date: Fri, 1 Jan 2016 17:13:58 +0100 Subject: [PATCH 160/169] Remove debug() statement --- src/PathautoGenerator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PathautoGenerator.php b/src/PathautoGenerator.php index f981610..245ca9b 100644 --- a/src/PathautoGenerator.php +++ b/src/PathautoGenerator.php @@ -144,7 +144,6 @@ public function createEntityAlias(EntityInterface $entity, $op) { $data = [ $this->tokenEntityMapper->getTokenTypeForEntityType($entity->getEntityTypeId()) => $entity, ]; - debug(array_keys($data)); // Allow other modules to alter the pattern. $context = array( From 561532019a99089fb4b0b4b0b8ea785343e4dc21 Mon Sep 17 00:00:00 2001 From: Juampy Date: Sun, 3 Jan 2016 20:52:05 +0100 Subject: [PATCH 161/169] [#113] Add tests for patterns with language conditions --- src/Tests/PathautoLocaleTest.php | 108 +++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index ed1e961..a6b6125 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -9,7 +9,11 @@ use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\language\Entity\ContentLanguageSettings; use Drupal\pathauto\PathautoState; use Drupal\simpletest\WebTestBase; @@ -27,14 +31,19 @@ class PathautoLocaleTest extends WebTestBase { * * @var array */ - public static $modules = array('node', 'pathauto', 'locale'); + public static $modules = array('node', 'pathauto', 'locale', 'content_translation'); /** - * Admin user. - * - * @var \Drupal\user\UserInterface + * {@inheritdoc} */ - protected $adminUser; + protected function setUp() { + parent::setUp(); + + // Create Article node types. + if ($this->profile != 'standard') { + $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); + } + } /** * Test that when an English node is updated, its old English alias is @@ -84,5 +93,92 @@ function testLanguageAliases() { // suffix. $this->assertEntityAlias($node, '/content/english-node-1', LanguageInterface::LANGCODE_NOT_SPECIFIED); } -} + /** + * Test that patterns work on multilingual content. + */ + function testLanguagePatterns() { + $this->drupalLogin($this->rootUser); + + // Add French language. + ConfigurableLanguage::createFromLangcode('fr')->save(); + + // Enable content translation on articles. + \Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE); + drupal_static_reset(); + \Drupal::entityManager()->clearCachedDefinitions(); + \Drupal::service('router.builder')->rebuild(); + \Drupal::service('entity.definition_update_manager')->applyUpdates(); + + // Create a pattern for English articles. + $pattern = $this->createPattern('node', '/the-articles/[node:title]'); + $pattern->addSelectionCondition([ + 'id' => 'entity_bundle:node', + 'bundles' => ['article' => 'article'], + 'negate' => FALSE, + 'context_mapping' => ['node' => 'node'], + ]); + $language_mapping = 'node:langcode:language'; + $pattern->addSelectionCondition([ + 'id' => 'language', + 'langcodes' => ['en' => 'en'], + 'negate' => FALSE, + 'context_mapping' => ['language' => $language_mapping], + ]); + $new_definition = new ContextDefinition('language', 'Language'); + $new_context = new Context($new_definition); + $pattern->addContext($language_mapping, $new_context); + $pattern->save(); + + // Create a pattern for French articles. + $pattern = $this->createPattern('node', '/les-articles/[node:title]'); + $pattern->addSelectionCondition([ + 'id' => 'entity_bundle:node', + 'bundles' => ['article' => 'article'], + 'negate' => FALSE, + 'context_mapping' => ['node' => 'node'], + ]); + $language_mapping = 'node:langcode:language'; + $pattern->addSelectionCondition([ + 'id' => 'language', + 'langcodes' => ['fr' => 'fr'], + 'negate' => FALSE, + 'context_mapping' => ['language' => $language_mapping], + ]); + $new_definition = new ContextDefinition('language', 'Language'); + $new_context = new Context($new_definition); + $pattern->addContext($language_mapping, $new_context); + $pattern->save(); + + // Create a node and its translation. Assert aliases. + $edit = array( + 'title[0][value]' => 'English node', + ); + $this->drupalPostForm('node/add/article', $edit, t('Save and publish')); + $this->drupalGet('node/1/edit'); + $english_node = $this->drupalGetNodeByTitle('English node'); + $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en'); + + $add_translation_url = Url::fromRoute('entity.node.content_translation_add', ['node' => $english_node->id(), 'source' => 'en', 'target' => 'fr']); + $edit = array( + 'title[0][value]' => 'French node', + ); + $this->drupalPostForm($add_translation_url, $edit, t('Save and keep published (this translation)')); + $this->rebuildContainer(); + $english_node = $this->drupalGetNodeByTitle('English node'); + $french_node = $english_node->getTranslation('fr'); + $this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr'); + + // Bulk delete and Bulk generate patterns. Assert aliases. + $this->deleteAllAliases(); + // Bulk create aliases. + $edit = array( + 'update[canonical_entities:node]' => TRUE, + ); + $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); + $this->assertText('Generated 1 URL alias.'); + $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en'); + $this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr'); + } + +} From 418934c1862ef993e71a4b52016f4fac66d17113 Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 4 Jan 2016 14:09:20 +0100 Subject: [PATCH 162/169] [#113] Add translation support on bulk alias generation --- src/PathautoGenerator.php | 11 ++++++----- src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php | 10 ++++++++++ src/Tests/PathautoLocaleTest.php | 11 ++++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/PathautoGenerator.php b/src/PathautoGenerator.php index 245ca9b..407070b 100644 --- a/src/PathautoGenerator.php +++ b/src/PathautoGenerator.php @@ -257,19 +257,20 @@ protected function getPatternByEntityType($entity_type_id) { * {@inheritdoc} */ public function getPatternByEntity(EntityInterface $entity) { - if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()])) { + $langcode = $entity->language()->getId(); + if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) { foreach ($this->getPatternByEntityType($entity->getEntityTypeId()) as $pattern) { if ($pattern->applies($entity)) { - $this->patterns[$entity->getEntityTypeId()][$entity->id()] = $pattern; + $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = $pattern; break; } } // If still not set. - if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()])) { - $this->patterns[$entity->getEntityTypeId()][$entity->id()] = NULL; + if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) { + $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = NULL; } } - return $this->patterns[$entity->getEntityTypeId()][$entity->id()]; + return $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode]; } /** diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 5c2c635..0b10f5d 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -179,6 +179,16 @@ protected function bulkUpdate(array $ids, array $options = array()) { $entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); foreach ($entities as $entity) { \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', $options); + + // Create aliases for the entity translations. + if ($entity->isTranslatable()) { + foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) { + if ($entity->hasTranslation($langcode)) { + $translated_entity = $entity->getTranslation($langcode); + \Drupal::service('pathauto.generator')->updateEntityAlias($translated_entity, 'bulkupdate', $options); + } + } + } } if (!empty($options['message'])) { diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index a6b6125..7dffaec 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -109,6 +109,10 @@ function testLanguagePatterns() { \Drupal::entityManager()->clearCachedDefinitions(); \Drupal::service('router.builder')->rebuild(); \Drupal::service('entity.definition_update_manager')->applyUpdates(); + // Enable the language selector when editing nodes. + $article_language_settings = \Drupal::service('entity.manager')->getStorage('language_content_settings')->load('node.article'); + $article_language_settings->set('language_alterable', 1); + $article_language_settings->save(); // Create a pattern for English articles. $pattern = $this->createPattern('node', '/the-articles/[node:title]'); @@ -153,17 +157,18 @@ function testLanguagePatterns() { // Create a node and its translation. Assert aliases. $edit = array( 'title[0][value]' => 'English node', + 'langcode[0][value]' => 'en', ); $this->drupalPostForm('node/add/article', $edit, t('Save and publish')); - $this->drupalGet('node/1/edit'); $english_node = $this->drupalGetNodeByTitle('English node'); $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en'); $add_translation_url = Url::fromRoute('entity.node.content_translation_add', ['node' => $english_node->id(), 'source' => 'en', 'target' => 'fr']); + $this->drupalGet($add_translation_url); $edit = array( 'title[0][value]' => 'French node', ); - $this->drupalPostForm($add_translation_url, $edit, t('Save and keep published (this translation)')); + $this->drupalPostForm(NULL, $edit, t('Save and keep published (this translation)')); $this->rebuildContainer(); $english_node = $this->drupalGetNodeByTitle('English node'); $french_node = $english_node->getTranslation('fr'); @@ -176,7 +181,7 @@ function testLanguagePatterns() { 'update[canonical_entities:node]' => TRUE, ); $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update')); - $this->assertText('Generated 1 URL alias.'); + $this->assertText(t('Generated 2 URL aliases.')); $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en'); $this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr'); } From bcab307a0f51aff031d96c078bffaa478b8f0d27 Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 4 Jan 2016 14:27:17 +0100 Subject: [PATCH 163/169] [#113] Use the UI to create patterns --- src/Tests/PathautoLocaleTest.php | 66 ++++++++++++++------------------ 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 7dffaec..4af2cd6 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -9,8 +9,6 @@ use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Plugin\Context\Context; -use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\language\Entity\ContentLanguageSettings; @@ -115,44 +113,36 @@ function testLanguagePatterns() { $article_language_settings->save(); // Create a pattern for English articles. - $pattern = $this->createPattern('node', '/the-articles/[node:title]'); - $pattern->addSelectionCondition([ - 'id' => 'entity_bundle:node', - 'bundles' => ['article' => 'article'], - 'negate' => FALSE, - 'context_mapping' => ['node' => 'node'], - ]); - $language_mapping = 'node:langcode:language'; - $pattern->addSelectionCondition([ - 'id' => 'language', - 'langcodes' => ['en' => 'en'], - 'negate' => FALSE, - 'context_mapping' => ['language' => $language_mapping], - ]); - $new_definition = new ContextDefinition('language', 'Language'); - $new_context = new Context($new_definition); - $pattern->addContext($language_mapping, $new_context); - $pattern->save(); + $this->drupalGet('admin/config/search/path/patterns/add'); + $edit = array( + 'type' => 'canonical_entities:node', + ); + $this->drupalPostAjaxForm(NULL, $edit, 'type'); + $edit += array( + 'pattern' => '/the-articles/[node:title]', + 'label' => 'English articles', + 'id' => 'english_articles', + 'bundles[article]' => TRUE, + 'languages[en]' => TRUE, + ); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Pattern English articles saved.'); // Create a pattern for French articles. - $pattern = $this->createPattern('node', '/les-articles/[node:title]'); - $pattern->addSelectionCondition([ - 'id' => 'entity_bundle:node', - 'bundles' => ['article' => 'article'], - 'negate' => FALSE, - 'context_mapping' => ['node' => 'node'], - ]); - $language_mapping = 'node:langcode:language'; - $pattern->addSelectionCondition([ - 'id' => 'language', - 'langcodes' => ['fr' => 'fr'], - 'negate' => FALSE, - 'context_mapping' => ['language' => $language_mapping], - ]); - $new_definition = new ContextDefinition('language', 'Language'); - $new_context = new Context($new_definition); - $pattern->addContext($language_mapping, $new_context); - $pattern->save(); + $this->drupalGet('admin/config/search/path/patterns/add'); + $edit = array( + 'type' => 'canonical_entities:node', + ); + $this->drupalPostAjaxForm(NULL, $edit, 'type'); + $edit += array( + 'pattern' => '/les-articles/[node:title]', + 'label' => 'French articles', + 'id' => 'french_articles', + 'bundles[article]' => TRUE, + 'languages[fr]' => TRUE, + ); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Pattern French articles saved.'); // Create a node and its translation. Assert aliases. $edit = array( From 1c6e7ed6dd6add23127d4196e6513f135b449fdf Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 4 Jan 2016 14:27:36 +0100 Subject: [PATCH 164/169] [#113] Always create the article content type --- src/Tests/PathautoLocaleTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 4af2cd6..d52bef9 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -37,10 +37,8 @@ class PathautoLocaleTest extends WebTestBase { protected function setUp() { parent::setUp(); - // Create Article node types. - if ($this->profile != 'standard') { - $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); - } + // Create Article node type. + $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); } /** From 8eb0019dec486b40156bd4d27c852bd82e0dd43d Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 4 Jan 2016 15:59:33 +0100 Subject: [PATCH 165/169] [#113] Fix failing assertion when testing patterns with languages --- src/Tests/PathautoLocaleTest.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index d52bef9..8d5199b 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -97,18 +97,20 @@ function testLanguagePatterns() { $this->drupalLogin($this->rootUser); // Add French language. - ConfigurableLanguage::createFromLangcode('fr')->save(); + // Add predefined French language. + $edit = array( + 'predefined_langcode' => 'fr', + ); + $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); // Enable content translation on articles. - \Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE); - drupal_static_reset(); - \Drupal::entityManager()->clearCachedDefinitions(); - \Drupal::service('router.builder')->rebuild(); - \Drupal::service('entity.definition_update_manager')->applyUpdates(); - // Enable the language selector when editing nodes. - $article_language_settings = \Drupal::service('entity.manager')->getStorage('language_content_settings')->load('node.article'); - $article_language_settings->set('language_alterable', 1); - $article_language_settings->save(); + $this->drupalGet('admin/config/regional/content-language'); + $edit = array( + 'entity_types[node]' => TRUE, + 'settings[node][article][translatable]' => TRUE, + 'settings[node][article][settings][language][language_alterable]' => TRUE, + ); + $this->drupalPostForm(NULL, $edit, t('Save configuration')); // Create a pattern for English articles. $this->drupalGet('admin/config/search/path/patterns/add'); @@ -151,8 +153,8 @@ function testLanguagePatterns() { $english_node = $this->drupalGetNodeByTitle('English node'); $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en'); - $add_translation_url = Url::fromRoute('entity.node.content_translation_add', ['node' => $english_node->id(), 'source' => 'en', 'target' => 'fr']); - $this->drupalGet($add_translation_url); + $this->drupalGet('node/' . $english_node->id() . '/translations'); + $this->clickLink(t('Add')); $edit = array( 'title[0][value]' => 'French node', ); From 96742c6e833aa1fb2702aa3b1e14f7c182f87d40 Mon Sep 17 00:00:00 2001 From: Juampy Date: Mon, 4 Jan 2016 16:07:40 +0100 Subject: [PATCH 166/169] [#113] Remove unneeded statements --- src/Tests/PathautoLocaleTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Tests/PathautoLocaleTest.php b/src/Tests/PathautoLocaleTest.php index 8d5199b..feffb25 100644 --- a/src/Tests/PathautoLocaleTest.php +++ b/src/Tests/PathautoLocaleTest.php @@ -9,9 +9,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\language\Entity\ContentLanguageSettings; use Drupal\pathauto\PathautoState; use Drupal\simpletest\WebTestBase; @@ -97,7 +95,6 @@ function testLanguagePatterns() { $this->drupalLogin($this->rootUser); // Add French language. - // Add predefined French language. $edit = array( 'predefined_langcode' => 'fr', ); From 462f3b9d9956ed5b220c5e5585887e46392cefa1 Mon Sep 17 00:00:00 2001 From: Juampy Date: Tue, 5 Jan 2016 22:31:01 +0100 Subject: [PATCH 167/169] [#113] Simplify bulk update process with translations --- .../pathauto/AliasType/EntityAliasTypeBase.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php index 0b10f5d..543901e 100644 --- a/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php +++ b/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php @@ -178,16 +178,10 @@ protected function bulkUpdate(array $ids, array $options = array()) { $entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids); foreach ($entities as $entity) { - \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', $options); - - // Create aliases for the entity translations. - if ($entity->isTranslatable()) { - foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) { - if ($entity->hasTranslation($langcode)) { - $translated_entity = $entity->getTranslation($langcode); - \Drupal::service('pathauto.generator')->updateEntityAlias($translated_entity, 'bulkupdate', $options); - } - } + // Update aliases for the entity's default language and its translations. + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + $translated_entity = $entity->getTranslation($langcode); + \Drupal::service('pathauto.generator')->updateEntityAlias($translated_entity, 'bulkupdate', $options); } } From a38509d3b0f5951969415b49b1388b5d975acd8d Mon Sep 17 00:00:00 2001 From: Lars Rackwitz-Rosenberg Date: Fri, 15 Jan 2016 12:13:36 +0100 Subject: [PATCH 168/169] [#128] Add drupal/token 8.1.x-dev as dependency to ensure code compatibility. --- composer.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0b1952a..a340280 100644 --- a/composer.json +++ b/composer.json @@ -2,5 +2,16 @@ "name": "drupal/pathauto", "description": "Provides a generic set of views plugins a mechanism for modules to automatically generate aliases for the content they manage.", "type": "drupal-module", - "license": "GPL-2.0+" + "license": "GPL-2.0+", + + "repositories": [ + { + "type": "composer", + "url": "https://packagist.drupal-composer.org" + } + ], + + "require": { + "drupal/token": "8.1.x-dev" + } } From 818240d12edf1c3edff70e321ec4c9822be90c1f Mon Sep 17 00:00:00 2001 From: Lars Rackwitz-Rosenberg Date: Fri, 15 Jan 2016 14:04:44 +0100 Subject: [PATCH 169/169] [#128] Added drupal/ctools 8.3.x-dev as dependency. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a340280..4d174bb 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ ], "require": { - "drupal/token": "8.1.x-dev" + "drupal/token": "8.1.x-dev", + "drupal/ctools": "8.3.x-dev" } }