Browse Source

updated contrib modules : translation_views, views_bulk_edit, views_bulk_operations, workflow, addtoany, redis, url_to_video_filter

Bachir Soussi Chiadmi 5 years ago
parent
commit
b8e8cc8cee
99 changed files with 4815 additions and 1007 deletions
  1. 0 10
      sites/all/modules/contrib/admin/workflow/config/schema/workflow.schema.yml
  2. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_access/workflow_access.info.yml
  3. 4 5
      sites/all/modules/contrib/admin/workflow/modules/workflow_access/workflow_access.module
  4. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_cleanup/workflow_cleanup.info.yml
  5. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_devel/workflow_devel.info.yml
  6. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_field/workflowfield.info.yml
  7. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_operations/workflow_operations.info.yml
  8. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_ui/src/Controller/WorkflowStateListBuilder.php
  9. 22 17
      sites/all/modules/contrib/admin/workflow/modules/workflow_ui/src/Form/WorkflowConfigTransitionRoleForm.php
  10. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.info.yml
  11. 3 3
      sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.module
  12. 12 1
      sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.routing.yml
  13. 79 0
      sites/all/modules/contrib/admin/workflow/src/Access/WorkflowHistoryAccess.php
  14. 1 78
      sites/all/modules/contrib/admin/workflow/src/Controller/WorkflowTransitionListController.php
  15. 16 15
      sites/all/modules/contrib/admin/workflow/src/Element/WorkflowTransitionElement.php
  16. 11 1
      sites/all/modules/contrib/admin/workflow/src/Entity/Workflow.php
  17. 31 31
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowConfigTransition.php
  18. 8 3
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowConfigTransitionInterface.php
  19. 1 1
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowInterface.php
  20. 5 13
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowManager.php
  21. 2 2
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowManagerInterface.php
  22. 13 4
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowScheduledTransition.php
  23. 21 52
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowState.php
  24. 35 41
      sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowTransition.php
  25. 1 3
      sites/all/modules/contrib/admin/workflow/src/Form/WorkflowTransitionForm.php
  26. 1 1
      sites/all/modules/contrib/admin/workflow/src/Plugin/Action/WorkflowNodeGivenStateAction.php
  27. 6 7
      sites/all/modules/contrib/admin/workflow/src/Plugin/Action/WorkflowStateActionBase.php
  28. 75 0
      sites/all/modules/contrib/admin/workflow/src/Plugin/Derivative/WorkflowLocalTask.php
  29. 0 1
      sites/all/modules/contrib/admin/workflow/src/Plugin/Field/FieldType/WorkflowItem.php
  30. 1 1
      sites/all/modules/contrib/admin/workflow/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php
  31. 2 3
      sites/all/modules/contrib/admin/workflow/src/Plugin/Validation/Constraint/WorkflowFieldConstraintValidator.php
  32. 136 0
      sites/all/modules/contrib/admin/workflow/src/Routing/RouteSubscriber.php
  33. 4 3
      sites/all/modules/contrib/admin/workflow/src/WorkflowTransitionListBuilder.php
  34. 103 0
      sites/all/modules/contrib/admin/workflow/src/WorkflowTypeAttributeTrait.php
  35. 16 0
      sites/all/modules/contrib/admin/workflow/workflow.field.inc
  36. 1 1
      sites/all/modules/contrib/admin/workflow/workflow.form.inc
  37. 3 3
      sites/all/modules/contrib/admin/workflow/workflow.info.yml
  38. 0 7
      sites/all/modules/contrib/admin/workflow/workflow.install
  39. 3 12
      sites/all/modules/contrib/admin/workflow/workflow.links.task.yml
  40. 63 55
      sites/all/modules/contrib/admin/workflow/workflow.module
  41. 0 33
      sites/all/modules/contrib/admin/workflow/workflow.routing.yml
  42. 5 5
      sites/all/modules/contrib/admin/workflow/workflow.services.yml
  43. 1 4
      sites/all/modules/contrib/dev/redis/.travis.yml
  44. 3 3
      sites/all/modules/contrib/dev/redis/redis.info.yml
  45. 2 1
      sites/all/modules/contrib/dev/redis/src/Cache/CacheBase.php
  46. 2 2
      sites/all/modules/contrib/dev/redis/src/Lock/Predis.php
  47. 26 0
      sites/all/modules/contrib/dev/redis/src/PersistentLock/Predis.php
  48. 1 1
      sites/all/modules/contrib/dev/redis/tests/src/Functional/Lock/RedisLockFunctionalTest.php
  49. 4 5
      sites/all/modules/contrib/dev/redis/tests/src/Functional/WebTest.php
  50. 3 3
      sites/all/modules/contrib/dev/redis/tests/src/Kernel/RedisQueueTest.php
  51. 3 3
      sites/all/modules/contrib/fields/addtoany/addtoany.info.yml
  52. 35 25
      sites/all/modules/contrib/fields/addtoany/addtoany.module
  53. 4 0
      sites/all/modules/contrib/fields/addtoany/config/install/addtoany.settings.yml
  54. 9 0
      sites/all/modules/contrib/fields/addtoany/css/addtoany.admin.css
  55. 91 2
      sites/all/modules/contrib/fields/addtoany/src/Form/AddToAnySettingsForm.php
  56. 1 1
      sites/all/modules/contrib/filters/url_to_video_filter/css/url_to_video_embed.scss
  57. 111 64
      sites/all/modules/contrib/filters/url_to_video_filter/js/vimeo_embed.js
  58. 99 54
      sites/all/modules/contrib/filters/url_to_video_filter/js/youtube_embed.js
  59. 129 131
      sites/all/modules/contrib/filters/url_to_video_filter/src/Plugin/Filter/FilterUrlToVideo.php
  60. 156 144
      sites/all/modules/contrib/filters/url_to_video_filter/src/Service/UrlToVideoFilterService.php
  61. 32 28
      sites/all/modules/contrib/filters/url_to_video_filter/src/Service/UrlToVideoFilterServiceInterface.php
  62. 3 5
      sites/all/modules/contrib/filters/url_to_video_filter/url_to_video_filter.info.yml
  63. 3 0
      sites/all/modules/contrib/filters/url_to_video_filter/url_to_video_filter.libraries.yml
  64. 22 13
      sites/all/modules/contrib/views/translation_views/README.md
  65. 7 8
      sites/all/modules/contrib/views/translation_views/config/optional/views.view.content_translations.yml
  66. 44 0
      sites/all/modules/contrib/views/translation_views/config/schema/translation_views.views.schema.yml
  67. 7 0
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationCountField.php
  68. 1 1
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationSourceLangcodeEqualsRowLangcodeField.php
  69. 1 1
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationStatus.php
  70. 7 0
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/filter/TranslationCountFilter.php
  71. 1 1
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/filter/TranslationSourceLangcodeEqualsRowLangcodeFilter.php
  72. 9 7
      sites/all/modules/contrib/views/translation_views/src/Plugin/views/join/TranslationLanguageJoin.php
  73. 1 7
      sites/all/modules/contrib/views/translation_views/src/TranslationCountTrait.php
  74. 996 0
      sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/test_views/views.view.comment_translation.yml
  75. 924 0
      sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/test_views/views.view.translation_views_all_filters_and_fields.yml
  76. 17 0
      sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/translation_views_test_views.info.yml
  77. 491 0
      sites/all/modules/contrib/views/translation_views/tests/src/Functional/CommentFullViewFiltersFieldsTest.php
  78. 471 0
      sites/all/modules/contrib/views/translation_views/tests/src/Functional/ContentFullViewFiltersFieldsTest.php
  79. 223 0
      sites/all/modules/contrib/views/translation_views/tests/src/Functional/ContentTranslationJobsViewTest.php
  80. 3 3
      sites/all/modules/contrib/views/translation_views/translation_views.info.yml
  81. 25 0
      sites/all/modules/contrib/views/translation_views/translation_views.module
  82. 16 15
      sites/all/modules/contrib/views/translation_views/translation_views.views.inc
  83. 6 0
      sites/all/modules/contrib/views/views_bulk_edit/README.txt
  84. 2 2
      sites/all/modules/contrib/views/views_bulk_edit/css/edit_form.css
  85. 45 4
      sites/all/modules/contrib/views/views_bulk_edit/src/Plugin/Action/ModifyEntityValues.php
  86. 3 3
      sites/all/modules/contrib/views/views_bulk_edit/views_bulk_edit.info.yml
  87. 4 1
      sites/all/modules/contrib/views/views_bulk_operations/js/frontUi.js
  88. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/modules/actions_permissions/actions_permissions.info.yml
  89. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/modules/views_bulk_operations_example/views_bulk_operations_example.info.yml
  90. 2 2
      sites/all/modules/contrib/views/views_bulk_operations/src/Form/ConfigureAction.php
  91. 4 3
      sites/all/modules/contrib/views/views_bulk_operations/src/Form/ConfirmAction.php
  92. 32 0
      sites/all/modules/contrib/views/views_bulk_operations/src/Form/ViewsBulkOperationsFormTrait.php
  93. 10 7
      sites/all/modules/contrib/views/views_bulk_operations/src/Plugin/views/field/ViewsBulkOperationsBulkForm.php
  94. 2 2
      sites/all/modules/contrib/views/views_bulk_operations/src/Service/ViewsBulkOperationsViewData.php
  95. 1 1
      sites/all/modules/contrib/views/views_bulk_operations/tests/src/Functional/ViewsBulkOperationsBulkFormTest.php
  96. 3 2
      sites/all/modules/contrib/views/views_bulk_operations/tests/src/Kernel/ViewsBulkOperationsKernelTestBase.php
  97. 0 3
      sites/all/modules/contrib/views/views_bulk_operations/tests/src/Unit/ViewsBulkOperationsBatchTest.php
  98. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/tests/views_bulk_operations_test/views_bulk_operations_test.info.yml
  99. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.info.yml

+ 0 - 10
sites/all/modules/contrib/admin/workflow/config/schema/workflow.schema.yml

@@ -66,17 +66,7 @@ field.storage_settings.workflow:
           label:
             type: label
             label: 'Label'
-    allowed_values_function:
-      type: string
-      label: 'Allowed values function'
 
 field.field_settings.workflow:
   type: mapping
   label: 'Workflow settings'
-
-workflow.settings:
-  type: config_object
-  mapping:
-    workflow_states_per_page:
-      type: integer
-      label: 'Number of workflow states displayed per page.'

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_access/workflow_access.info.yml

@@ -6,8 +6,8 @@ package: Workflow
 # core: 8.x
 type: module
 
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 4 - 5
sites/all/modules/contrib/admin/workflow/modules/workflow_access/workflow_access.module

@@ -66,7 +66,7 @@ function workflow_access_workflow_operations($op, EntityInterface $entity = NULL
 }
 
 /**
- * Implements hook_entity_insert().
+ * Implements hook_ENTITY_TYPE_insert().
  *
  * We use the Role weight as an id.
  * In contrary to content_access module, that uses a 'content_access_roles_gids'
@@ -78,11 +78,11 @@ function workflow_access_user_role_insert(EntityInterface $entity) {
   // Attend user to Rebuild data, because the weight of a role
   // is the key for workflow_Access.
   /** @var $entity \Drupal\user\RoleInterface */
-   node_access_needs_rebuild(TRUE);
+  node_access_needs_rebuild(TRUE);
 }
 
 /**
- * Implements hook_access_entity_update().
+ * Implements hook_access_ENTITY_TYPE_update().
  *
  * @param EntityInterface $entity
  */
@@ -167,8 +167,7 @@ function workflow_access_node_access_records(\Drupal\node\NodeInterface $node) {
     // - when rebuilding permissions via batch for Workflow Fields.
     // In that case, we need to create the workflow_transitions ourselves to
     // calculate the grants.
-    foreach (_workflow_info_fields($node, $entity_type) as $field) {
-      $field_name = $field->getName(); // Do not use id().
+    foreach (workflow_get_workflow_field_names($node, $entity_type) as $field_name) {
       $old_sid = $new_sid = $node->$field_name->value;
 
       // Create a dummy transition, just to set $node->workflow_transitions.

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_cleanup/workflow_cleanup.info.yml

@@ -6,8 +6,8 @@ package: Workflow
 dependencies:
   - workflow
 configure: workflow.cleanup.settings
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_devel/workflow_devel.info.yml

@@ -7,8 +7,8 @@ type: module
 dependencies:
   - workflow
 
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_field/workflowfield.info.yml

@@ -5,8 +5,8 @@ type: module
 # core: 8.x
 
 hidden: TRUE
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_operations/workflow_operations.info.yml

@@ -7,8 +7,8 @@ type: module
 dependencies:
   - workflow
 
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_ui/src/Controller/WorkflowStateListBuilder.php

@@ -24,16 +24,16 @@ class WorkflowStateListBuilder extends DraggableListBuilder {
     $entities = [];
 
     // Get the Workflow from the page.
-    /** @var $workflow \Drupal\workflow\Entity\Workflow */
     if (!$workflow = workflow_url_get_workflow()) {
       // @todo: Generate error message.
       return $entities;
     }
-    $wid = $url_wid = $workflow->id();
 
+    $wid = $workflow->id();
+    /** @var WorkflowState[] $entities */
     $entities = parent::load();
     foreach ($entities as $key => $entity) {
-      if (!isset($entity->wid) || $entity->wid != $wid) {
+      if ($entity->getWorkflowId() != $wid) {
         unset($entities[$key]);
       }
     }

+ 22 - 17
sites/all/modules/contrib/admin/workflow/modules/workflow_ui/src/Form/WorkflowConfigTransitionRoleForm.php

@@ -99,20 +99,19 @@ class WorkflowConfigTransitionRoleForm extends WorkflowConfigTransitionFormBase
               continue;
             }
             $to_sid = $to_state->id();
-            $stay_on_this_state = ($to_sid == $from_sid);
 
             // Load existing config_transitions. Create if not found.
             $config_transitions = $workflow->getTransitionsByStateId($from_sid, $to_sid);
             if (!$config_transition = reset($config_transitions)) {
               $config_transition = $workflow->createTransition($from_sid, $to_sid);
             }
+            $stay_on_this_state = !$config_transition->hasStateChange();
 
-            $row[$to_sid]['workflow_config_transition'] = ['#type' => 'value', '#value' => $config_transition, ];
+            $row[$to_sid]['workflow_config_transition'] = ['#type' => 'value', '#value' => $config_transition,];
             $row[$to_sid]['roles'] = [
-              '#type' => $stay_on_this_state ? 'checkboxes' : 'checkboxes',
+              '#type' => 'checkboxes',
               '#options' => $stay_on_this_state ? [] : $roles,
               '#disabled' => $stay_on_this_state,
-              // When $stay_on_this_state, allow all roles.
               '#default_value' => $stay_on_this_state ? $allow_all_roles : $config_transition->roles,
             ];
           }
@@ -127,24 +126,30 @@ class WorkflowConfigTransitionRoleForm extends WorkflowConfigTransitionFormBase
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
     $workflow = $this->workflow;
+
+    // If only the 'Creation' state is available,
+    if (count($form_state->getValue($this->entitiesKey)) < 2) {
+      $form_state->setErrorByName('id', t('Please create at least one other state.',
+        []));
+    }
+
     // Make sure 'author' is checked for (creation) -> [something].
     $creation_state = $workflow->getCreationState();
+    $creation_state_id = $workflow->getCreationState()->id();
+    $author_has_permission = FALSE;
+    foreach ($form_state->getValue($this->entitiesKey) as $from_sid => $to_data) {
+      foreach ($to_data as $to_sid => $transition_data) {
 
-    if (empty($form_state->getValue($this->entitiesKey))) {
-      $author_has_permission = TRUE;
-    }
-    else {
-      $author_has_permission = FALSE;
-      foreach ($form_state->getValue($this->entitiesKey) as $from_sid => $to_data) {
-        foreach ($to_data as $to_sid => $transition_data) {
-          if ($from_sid == $to_sid) {
-            // Same-state-transition do not count.
-          }
-          elseif (!empty($transition_data['roles'][WORKFLOW_ROLE_AUTHOR_RID])) {
-            $author_has_permission = TRUE;
+        if ($from_sid == $creation_state_id) {
+          // Same-state-transitions do not count.
+          if ($from_sid != $to_sid) {
+            if (!empty($transition_data['roles'][WORKFLOW_ROLE_AUTHOR_RID])) {
+              $author_has_permission = TRUE;
+            }
             break;
           }
         }
+
       }
     }
     if (!$author_has_permission) {
@@ -152,7 +157,7 @@ class WorkflowConfigTransitionRoleForm extends WorkflowConfigTransitionFormBase
         ['%creation' => $creation_state->label()]));
     }
 
-    return;
+    parent::validateForm($form, $form_state);
   }
 
   /**

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.info.yml

@@ -10,8 +10,8 @@ configure: entity.workflow_type.collection
 dependencies:
   - workflow
 
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 3 - 3
sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.module

@@ -70,8 +70,8 @@ function workflow_ui_help($route_name, RouteMatchInterface $route_match) {
  *
  * @deprecated : @see workflow_url_get_workflow .
  */
-function workflow_ui_url_get_workflow($url = '' ) {
-  return workflow_url_get_workflow($url);
+function workflow_ui_url_get_workflow() {
+  return workflow_url_get_workflow();
 }
 
 /**
@@ -88,6 +88,6 @@ function workflow_ui_url_get_title() {
  *
  * @deprecated : @see workflow_url_get_form_type .
  */
-function workflow_ui_url_get_form_type($url = '' ) {
+function workflow_ui_url_get_form_type($url = '') {
   return workflow_url_get_form_type($url);
 }

+ 12 - 1
sites/all/modules/contrib/admin/workflow/modules/workflow_ui/workflow_ui.routing.yml

@@ -23,7 +23,10 @@ entity.workflow_state.collection:
     _title_callback: 'workflow_url_get_title'
   requirements:
     _permission: 'administer workflow'
-
+  options:
+    parameters:
+      workflow_type:
+        type: entity:workflow_type
 
 ### Workflow Transitions
 entity.workflow_transition.collection:
@@ -34,6 +37,10 @@ entity.workflow_transition.collection:
     _title_callback: 'workflow_url_get_title'
   requirements:
     _permission: 'administer workflow'
+  options:
+    parameters:
+      workflow_type:
+        type: entity:workflow_type
 
 ### Workflow Labels
 entity.workflow_label.collection:
@@ -44,3 +51,7 @@ entity.workflow_label.collection:
     _title_callback: 'workflow_url_get_title'
   requirements:
     _permission: 'administer workflow'
+  options:
+    parameters:
+      workflow_type:
+        type: entity:workflow_type

+ 79 - 0
sites/all/modules/contrib/admin/workflow/src/Access/WorkflowHistoryAccess.php

@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\workflow\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\workflow\Entity\WorkflowManager;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Checks access to Workflow tab.
+ */
+class WorkflowHistoryAccess implements AccessInterface {
+
+  /**
+   * Check if the user has permissions to view this workflow.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   Current user account.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch
+   *   Current routeMatch.
+   * @param \Symfony\Component\Routing\Route $route
+   *   Current route.
+   *
+   * @return \Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden
+   *   If the user can access to this workflow.
+   */
+  public function access(AccountInterface $account, RouteMatchInterface $routeMatch, Route $route) {
+    static $access = [];
+
+    $uid = ($account) ? $account->id() : -1;
+
+    $entity = workflow_url_get_entity();
+    $entity_id = $entity->id();
+    $entity_type = $entity->getEntityTypeId();
+    $entity_bundle = $entity->bundle();
+    $field_name = workflow_url_get_parameter('field_name'); // @todo: this doesn't work.
+
+    if (isset($access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'])) {
+      return $access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'];
+    }
+
+    $access_result = AccessResult::forbidden();
+
+    // When having multiple workflows per bundle, use Views display
+    // 'Workflow history per entity' instead!
+    $fields = _workflow_info_fields($entity, $entity_type, $entity_bundle, $field_name);
+    if (!$fields) {
+      return AccessResult::forbidden();
+    }
+
+    // @todo: Keep below code aligned between WorkflowState, ~Transition, ~TransitionListController
+    // Determine if user is owner of the entity.
+    $is_owner = WorkflowManager::isOwner($account, $entity);
+
+    /*
+     * Determine if user has Access. Fill the cache.
+     */
+    // @todo: what to do with multiple workflow_fields per bundle? Use Views instead! Or introduce a setting.
+    // @todo D8-port: workflow_tab_access: use proper 'WORKFLOW_TYPE' permissions
+    foreach ($fields as $definition) {
+      $type_id = $definition->getSetting('workflow_type');
+      if ($account->hasPermission("access any $type_id workflow_transion overview")) {
+        $access_result = AccessResult::allowed();
+      }
+      elseif ($is_owner && $account->hasPermission("access own $type_id workflow_transion overview")) {
+        $access_result = AccessResult::allowed();
+      }
+      elseif ($account->hasPermission('administer nodes')) {
+        $access_result = AccessResult::allowed();
+      }
+      $access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'] = $access_result;
+    }
+
+    return $access_result;
+  }
+}

+ 1 - 78
sites/all/modules/contrib/admin/workflow/src/Controller/WorkflowTransitionListController.php

@@ -58,7 +58,7 @@ class WorkflowTransitionListController extends EntityListController implements C
 
   /**
    * Generates an overview table of older revisions of a node,
-   * but only if this::historyAccess() allows it.
+   * but only if WorkflowHistoryAccess::access() allows it.
    *
    * @param EntityInterface $node
    *   A node object.
@@ -137,81 +137,4 @@ class WorkflowTransitionListController extends EntityListController implements C
     return $form;
   }
 
-  /**
-   * Menu access control callback. Checks access to Workflow tab.
-   *
-   * This used to be D7-function workflow_tab_access($user, $entity).
-   *
-   * The History tab should not be used with multiple workflows per entity.
-   * Use the dedicated view for this use case.
-   * @todo D8: remove this in favour of View 'Workflow history per entity'.
-   * @todo D8-port: make this workflow for non-Node entity types.
-   *
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   Run access checks for this account.
-   *
-   * @return \Drupal\Core\Access\AccessResult
-   */
-  public function historyAccess(AccountInterface $account) {
-    static $access = [];
-
-    $uid = ($account) ? $account->id() : -1;
-
-    // @todo D8-port: make Workflow History tab happen for every entity_type.
-    // @see workflow.routing.yml, workflow.links.task.yml, WorkflowTransitionListController.
-    // ATM it only works for Nodes and Terms.
-    // This is a hack. The Route should always pass an object.
-    // On view tab, $entity is object,
-    // On workflow tab, $entity is id().
-    // Get the entity for this form.
-    $entity = workflow_url_get_entity();
-
-    /* @var $entity EntityInterface */
-    // Figure out the $entity's bundle and id.
-    $entity_type = $entity->getEntityTypeId();
-    $entity_bundle = $entity->bundle();
-    $entity_id = ($entity) ? $entity->id() : '';
-    $field_name = workflow_url_get_field_name();
-
-    if (isset($access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'])) {
-      return $access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'];
-    }
-
-    $access_result = AccessResult::forbidden();
-
-    // When having multiple workflows per bundle, use Views display
-    // 'Workflow history per entity' instead!
-    $fields = _workflow_info_fields($entity, $entity_type, $entity_bundle, $field_name);
-    if (!$fields) {
-      return AccessResult::forbidden();
-    }
-    else {
-      // @todo: Keep below code aligned between WorkflowState, ~Transition, ~TransitionListController
-      $uid = ($account) ? $account->id() : -1;
-      $entity_id = ($entity) ? $entity->id() : '';
-      // Determine if user is owner of the entity.
-      $is_owner = WorkflowManager::isOwner($account, $entity);
-
-      /**
-       * Determine if user has Access. Fill the cache.
-       */
-      // @todo: what to do with multiple workflow_fields per bundle? Use Views instead! Or introduce a setting.
-      // @todo D8-port: workflow_tab_access: use proper 'WORKFLOW_TYPE' permissions
-      foreach ($fields as $definition) {
-        $type_id = $definition->getSetting('workflow_type');
-        if ($account->hasPermission("access any $type_id workflow_transion overview")) {
-          $access_result = AccessResult::allowed();
-        }
-        elseif ($is_owner && $account->hasPermission("access own $type_id workflow_transion overview")) {
-          $access_result = AccessResult::allowed();
-        }
-        elseif ($account->hasPermission('administer nodes')) {
-          $access_result = AccessResult::allowed();
-        }
-        $access[$uid][$entity_type][$entity_id][$field_name ? $field_name : 'no_field'] = $access_result;
-      }
-    }
-    return $access_result;
-  }
-
 }

+ 16 - 15
sites/all/modules/contrib/admin/workflow/src/Element/WorkflowTransitionElement.php

@@ -41,7 +41,7 @@ class WorkflowTransitionElement extends FormElement {
    * @param array $complete_form
    */
   public static function validateTransition(&$element, FormStateInterface $form_state, &$complete_form) {
-    workflow_debug( __FILE__, __FUNCTION__, __LINE__); // @todo D8-port: still test this snippet.
+    workflow_debug(__FILE__, __FUNCTION__, __LINE__); // @todo D8-port: still test this snippet.
   }
 
   /**
@@ -57,7 +57,7 @@ class WorkflowTransitionElement extends FormElement {
    *   The Workflow element
    */
   public static function processTransition(&$element, FormStateInterface $form_state, &$complete_form) {
-    workflow_debug( __FILE__, __FUNCTION__, __LINE__); // @todo D8-port: still test this snippet.
+    workflow_debug(__FILE__, __FUNCTION__, __LINE__); // @todo D8-port: still test this snippet.
     return self::transitionElement($element, $form_state, $complete_form);
   }
 
@@ -159,13 +159,6 @@ class WorkflowTransitionElement extends FormElement {
       $default_value = FALSE;
     }
 
-    // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.).
-    // Make it uniquer by adding the field name, or else the scheduling of
-    // multiple workflow_fields is not independent of each other.
-    // If we are indeed on a Transition form (so, not a Node Form with widget)
-    // then change the form id, too.
-    $form_id = self::getFormId();
-
     /*
      * Output: generate the element.
      */
@@ -230,7 +223,7 @@ class WorkflowTransitionElement extends FormElement {
 
     // Add a state formatter before the rest of the form,
     // when transition is scheduled or widget is hidden.
-    if ( (!$show_widget) || $transition_is_scheduled || $transition->isExecuted()) {
+    if ((!$show_widget) || $transition_is_scheduled || $transition->isExecuted()) {
       $element['workflow_current_state'] = workflow_state_formatter($entity, $field_name, $current_sid);
       // Set a proper weight, which works for Workflow Options in select list AND action buttons.
       $element['workflow_current_state']['#weight'] = -0.005;
@@ -316,6 +309,14 @@ class WorkflowTransitionElement extends FormElement {
         '#type' => 'container',
         '#tree' => TRUE,
       ];
+      // Define class for '#states' behaviour.
+      // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.).
+      // Make it uniquer by adding the field name, or else the scheduling of
+      // multiple workflow_fields is not independent of each other.
+      // If we are indeed on a Transition form (so, not a Node Form with widget)
+      // then change the form id, too.
+      $form_id = $form_state->getBuildInfo()['form_id'];
+      $class_identifier = Html::getClass('scheduled_' . Html::getUniqueId($form_id).'-'.$field_name);
       $element['workflow_scheduling']['scheduled'] = [
         '#type' => 'radios',
         '#title' => t('Schedule'),
@@ -326,7 +327,7 @@ class WorkflowTransitionElement extends FormElement {
         '#default_value' => $transition_is_scheduled ? '1' : '0',
         '#attributes' => [
           // 'id' => 'scheduled_' . $form_id,
-          'class' => [Html::getClass('scheduled_' . $form_id)],
+          'class' => [$class_identifier],
         ],
       ];
       $element['workflow_scheduling']['date_time'] = [
@@ -337,18 +338,18 @@ class WorkflowTransitionElement extends FormElement {
         '#suffix' => '</div>',
         '#states' => [
           //'visible' => array(':input[id="' . 'scheduled_' . $form_id . '"]' => array('value' => '1')),
-          'visible' => ['input.' . Html::getClass('scheduled_' . $form_id) => ['value' => '1']],
+          'visible' => ['input.' . $class_identifier => ['value' => '1']],
         ],
       ];
       $element['workflow_scheduling']['date_time']['workflow_scheduled_date'] = [
         '#type' => 'date',
         '#prefix' => t('At'),
-        '#default_value' => implode( '-', [
+        '#default_value' => implode('-', [
             'year' => date('Y', $timestamp),
             'month' => date('m', $timestamp),
             'day' => date('d', $timestamp),
           ]
-        )
+        ),
       ];
       $element['workflow_scheduling']['date_time']['workflow_scheduled_hour'] = [
         '#type' => 'textfield',
@@ -419,7 +420,7 @@ class WorkflowTransitionElement extends FormElement {
     return 'workflow_transition_form'; //@todo D8-port: add $form_id for widget and History tab.
   }
 
-    /**
+  /**
    * Implements ContentEntityForm::copyFormValuesToEntity(), and is called from:
    * - WorkflowTransitionForm::copyFormValuesToEntity()
    * - WorkflowDefaultWidget

+ 11 - 1
sites/all/modules/contrib/admin/workflow/src/Entity/Workflow.php

@@ -84,6 +84,13 @@ class Workflow extends ConfigEntityBase implements WorkflowInterface {
   public $states = [];
   public $transitions = [];
 
+  /**
+   * The module implementing this object, for config_export.
+   *
+   * @var string
+   */
+  protected $module = 'workflow';
+
   /**
    * CRUD functions.
    */
@@ -135,7 +142,10 @@ class Workflow extends ConfigEntityBase implements WorkflowInterface {
    */
   public function delete() {
     if (!$this->isDeletable()) {
-      // @todo: throw error if not workflow->isDeletable().
+      $message = t('Workflow %workflow is not Deletable. Please delete the field where this workflow type is reffered',
+        ['%workflow' => $this->label()]);
+      drupal_set_message($message, 'error');
+      return;
     }
     else {
       // Delete associated state (also deletes any associated transitions).

+ 31 - 31
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowConfigTransition.php

@@ -5,6 +5,7 @@ namespace Drupal\workflow\Entity;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\user\UserInterface;
+use Drupal\workflow\WorkflowTypeAttributeTrait;
 
 /**
  * Workflow configuration entity to persistently store configuration.
@@ -45,6 +46,10 @@ use Drupal\user\UserInterface;
  * )
  */
 class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfigTransitionInterface {
+  /*
+   * Add variables and get/set methods for Workflow property.
+   */
+  use WorkflowTypeAttributeTrait;
 
   // Transition data.
   public $id;
@@ -52,11 +57,12 @@ class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfi
   public $to_sid;
   public $roles = [];
 
-  // Extra fields.
-  protected $wid;
-  // The following must explicitly defined, and not be public, to avoid errors
-  // when exporting with json_encode().
-  protected $workflow = NULL;
+  /**
+   * The module implementing this object, for config_export.
+   *
+   * @var string
+   */
+  protected $module = 'workflow';
 
   /*
    * Entity class functions.
@@ -67,7 +73,11 @@ class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfi
    */
   public function __construct(array $values = [], $entityType = NULL) {
     // Please be aware that $entity_type and $entityType are different things!
-    return parent::__construct($values, $entity_type = 'workflow_config_transition');
+    parent::__construct($values, $entity_type = 'workflow_config_transition');
+    $state = WorkflowState::load($this->to_sid ? $this->to_sid : $this->from_sid);
+    if($state) {
+      $this->setWorkflow($state->getWorkflow());
+    }
   }
 
   /**
@@ -76,8 +86,10 @@ class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfi
    * @param $to_sid
    */
   public function setValues($from_sid, $to_sid) {
-    $this->from_sid = $from_sid;
-    $this->to_sid = $to_sid;
+    $state = WorkflowState::load($this->to_sid ? $this->to_sid : $this->from_sid);
+    if($state) {
+      $this->setWorkflow($state->getWorkflow());
+    }
   }
 
   /**
@@ -148,29 +160,6 @@ class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfi
    * Property functions.
    */
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getWorkflow() {
-    if (!$this->workflow && $wid = $this->getWorkflowId()) {
-      $this->workflow = Workflow::load($wid);
-    }
-    return $this->workflow;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getWorkflowId() {
-    if (!$this->wid) {
-      $from_sid = $this->getFromSid();
-      $to_sid = $this->getToSid();
-      $state = WorkflowState::load($to_sid ? $to_sid : $from_sid);
-      $this->wid = $state->getWorkflowId();
-    }
-    return $this->wid;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -219,4 +208,15 @@ class WorkflowConfigTransition extends ConfigEntityBase implements WorkflowConfi
     return TRUE == array_intersect($user->getRoles(), $this->roles);
   }
 
+  /**
+   * Determines if the State changes by this Transition.
+   * @return bool
+   */
+  public function hasStateChange() {
+    if ($this->from_sid == $this->to_sid) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
 }

+ 8 - 3
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowConfigTransitionInterface.php

@@ -31,7 +31,7 @@ interface WorkflowConfigTransitionInterface {
   public function isAllowed(UserInterface $user, $force = FALSE);
 
   /**
-   * Returns the Workflow object of this State.
+   * Returns the Workflow object of this object.
    *
    * @return Workflow
    *   Workflow object.
@@ -39,10 +39,10 @@ interface WorkflowConfigTransitionInterface {
   public function getWorkflow();
 
   /**
-   * Returns the Workflow ID of this Transition
+   * Returns the Workflow ID of this object.
    *
    * @return string
-   *   Workflow Id.
+   *   Workflow ID.
    */
   public function getWorkflowId();
 
@@ -66,4 +66,9 @@ interface WorkflowConfigTransitionInterface {
    */
   public function getToSid();
 
+  /**
+   * Determines if the State changes by this Transition.
+   * @return bool
+   */
+  public function hasStateChange();
 }

+ 1 - 1
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowInterface.php

@@ -130,7 +130,7 @@ interface WorkflowInterface {
    * @param string $to_sid
    * @param array $values
    *
-   * @return mixed|null|static
+   * @return WorkflowConfigTransitionInterface
    */
   public function createTransition($from_sid, $to_sid, $values = []);
 

+ 5 - 13
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowManager.php

@@ -140,21 +140,13 @@ class WorkflowManager implements WorkflowManagerInterface {
   public static function executeTransitionsOfEntity(EntityInterface $entity) {
 
     // Avoid this hook on workflow objects.
-    if (in_array($entity->getEntityTypeId(), [
-      'workflow_type',
-      'workflow_state',
-      'workflow_config_transition',
-      'workflow_transition',
-      'workflow_scheduled_transition',
-    ])) {
+    if (WorkflowManager::isWorkflowEntityType($entity->getEntityTypeId())) {
       return;
     }
 
     $user = workflow_current_user();
 
-    foreach (_workflow_info_fields($entity) as $field_info) {
-      $field_name = $field_info->getName();
-
+    foreach (workflow_get_workflow_field_names($entity) as $field_name) {
       // Transition is created in widget or WorkflowTransitionForm.
       /** @var $transition WorkflowTransitionInterface */
       $transition = $entity->$field_name->__get('workflow_transition');
@@ -345,7 +337,7 @@ class WorkflowManager implements WorkflowManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public static function getWorkflowTransitionForm(EntityInterface $entity, string $field_name) {
+  public static function getWorkflowTransitionForm(EntityInterface $entity, $field_name, array $form_state_additions = []) {
     // Create a transition, to pass to the form. No need to use setValues().
     $current_sid = workflow_node_current_state($entity, $field_name);
     $transition = WorkflowTransition::create([$current_sid, 'field_name' => $field_name]);
@@ -353,7 +345,7 @@ class WorkflowManager implements WorkflowManagerInterface {
     // Create the WorkflowTransitionForm.
     /** @var \Drupal\Core\Entity\EntityFormBuilder $entity_form_builder */
     $entity_form_builder = \Drupal::getContainer()->get('entity.form_builder');
-    $form = $entity_form_builder->getForm($transition, 'add');
+    $form = $entity_form_builder->getForm($transition, 'add', $form_state_additions);
     return $form;
   }
 
@@ -455,7 +447,7 @@ class WorkflowManager implements WorkflowManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public static function isWorkflowEntityType(string $entity_type_id) {
+  public static function isWorkflowEntityType($entity_type_id) {
     return in_array($entity_type_id, [
       'workflow_type',
       'workflow_state',

+ 2 - 2
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowManagerInterface.php

@@ -108,7 +108,7 @@ interface WorkflowManagerInterface {
    *
    * @return
    */
-  public static function getWorkflowTransitionForm(EntityInterface $entity, string $field_name);
+  public static function getWorkflowTransitionForm(EntityInterface $entity, $field_name, array $form_state_additions = []);
 
   /**
    * Returns the attached fields (via Field UI)
@@ -168,6 +168,6 @@ interface WorkflowManagerInterface {
    *
    * @return bool
    */
-  public static function isWorkflowEntityType(string $entity_type_id);
+  public static function isWorkflowEntityType($entity_type_id);
 
 }

+ 13 - 4
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowScheduledTransition.php

@@ -47,7 +47,7 @@ use Drupal\Core\Field\BaseFieldDefinition;
 class WorkflowScheduledTransition extends WorkflowTransition {
 
   /**
-   * Constructor.
+   * @inheritdoc.
    */
   public function __construct(array $values = [], $entityType = 'workflow_scheduled_transition', $bundle = FALSE, $translations = []) {
     // Please be aware that $entity_type and $entityType are different things!
@@ -70,7 +70,7 @@ class WorkflowScheduledTransition extends WorkflowTransition {
    * {@inheritdoc}
    *
    * This is a hack to avoid the following error, because ScheduledTransition is not a bundle of Workflow:
-   *   Drupal\Component\Plugin\Exception\PluginNotFoundException: The "entity:workflow_scheduled_transition:eerste" plugin does not exist. in Drupal\Core\Plugin\DefaultPluginManager->doGetDefinition() (line 60 of core\lib\Drupal\Component\Plugin\Discovery\DiscoveryTrait.php).
+   *   Drupal\Component\Plugin\Exception\PluginNotFoundException: The "entity:workflow_scheduled_transition:first" plugin does not exist. in Drupal\Core\Plugin\DefaultPluginManager->doGetDefinition() (line 60 of core\lib\Drupal\Component\Plugin\Discovery\DiscoveryTrait.php).
    */
   function validate() {
     // Since this function generates an error in one use case (using WorkflowTransitionForm)
@@ -176,11 +176,13 @@ class WorkflowScheduledTransition extends WorkflowTransition {
    *
    * @param int $start
    * @param int $end
+   * @param string $from_sid
+   * @param string $to_sid
    *
    * @return WorkflowScheduledTransition[]
    *   An array of transitions.
    */
-  public static function loadBetween($start = 0, $end = 0) {
+  public static function loadBetween($start = 0, $end = 0, $from_sid = '', $to_sid = '') {
     $transition_type = 'workflow_scheduled_transition'; // @todo: get this from annotation.
 
     /* @var $query \Drupal\Core\Entity\Query\QueryInterface */
@@ -193,6 +195,12 @@ class WorkflowScheduledTransition extends WorkflowTransition {
     if ($end) {
       $query->condition('timestamp', $end, '<');
     }
+    if ($from_sid) {
+      $query->condition('from_sid', $from_sid, '=');
+    }
+    if ($to_sid) {
+      $query->condition('to_sid', $from_sid, '=');
+    }
 
     $ids = $query->execute();
     $transitions = self::loadMultiple($ids);
@@ -233,7 +241,8 @@ class WorkflowScheduledTransition extends WorkflowTransition {
     $fields['timestamp'] = BaseFieldDefinition::create('created')
       ->setLabel(t('Scheduled'))
       ->setDescription(t('The date+time this transition is scheduled for.'))
-      ->setQueryable(FALSE)
+//      ->setQueryable(FALSE)
+//      ->setCustomStorage(FALSE)
 //      ->setTranslatable(TRUE)
 //      ->setDisplayOptions('view', array(
 //        'label' => 'hidden',

+ 21 - 52
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowState.php

@@ -8,6 +8,7 @@ use Drupal\Core\Entity\Entity;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\workflow\WorkflowTypeAttributeTrait;
 
 /**
  * Workflow configuration entity to persistently store configuration.
@@ -54,6 +55,11 @@ use Drupal\Core\Language\LanguageInterface;
  */
 class WorkflowState extends ConfigEntityBase {
 
+  /*
+   * Add variables and get/set methods for Workflow property.
+   */
+  use WorkflowTypeAttributeTrait;
+
   /**
    * The machine name.
    *
@@ -68,13 +74,6 @@ class WorkflowState extends ConfigEntityBase {
    */
   public $label;
 
-  /**
-   * The machine_name of the attached Workflow.
-   *
-   * @var string
-   */
-  public $wid;
-
   /**
    * The weight of this Workflow state.
    *
@@ -89,11 +88,11 @@ class WorkflowState extends ConfigEntityBase {
   public $status = 1;
 
   /**
-   * The attached Workflow.
+   * The module implementing this object, for config_export.
    *
-   * @var Workflow
+   * @var string
    */
-  protected $workflow;
+  protected $module = 'workflow';
 
   /**
    * CRUD functions.
@@ -133,7 +132,7 @@ class WorkflowState extends ConfigEntityBase {
     // Create the machine_name for new states.
     // N.B.: Keep machine_name in WorkflowState and ~ListBuilder aligned.
     $sid = $this->id();
-    $wid = $this->wid;
+    $wid = $this->getWorkflowId();
     $label = $this->label();
 
     // Set the workflow-including machine_name.
@@ -185,7 +184,7 @@ class WorkflowState extends ConfigEntityBase {
     $result = [];
     foreach ($states as $state) {
       /** @var  WorkflowState $state */
-      if ((!$wid) || ($wid == $state->wid)) {
+      if ((!$wid) || ($wid == $state->getWorkflowId())) {
         $result[$state->id()] = $state;
       }
     }
@@ -198,8 +197,8 @@ class WorkflowState extends ConfigEntityBase {
   public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
     /** @var WorkflowState $a */
     /** @var WorkflowState $b */
-    $a_wid = $a->wid;
-    $b_wid = $b->wid;
+    $a_wid = $a->getWorkflowId();
+    $b_wid = $b->getWorkflowId();
     if ($a_wid == $b_wid) {
       $a_weight = $a->getWeight();
       $b_weight = $b->getWeight();
@@ -263,7 +262,7 @@ class WorkflowState extends ConfigEntityBase {
     }
 
     // Delete the transitions this state is involved in.
-    $workflow = Workflow::load($this->wid);
+    $workflow = Workflow::load($this->getWorkflowId());
     /** @var WorkflowInterface $workflow */
     /** @var WorkflowTransitionInterface $transition */
     foreach ($workflow->getTransitionsByStateId($current_sid, '') as $transition) {
@@ -295,39 +294,6 @@ class WorkflowState extends ConfigEntityBase {
     return $this->weight;
   }
 
-  /**
-   * Returns the Workflow ID of this State.
-   *
-   * @return string
-   *   Workflow Id.
-   */
-  public function getWorkflowId() {
-    return $this->wid;
-  }
-
-  /**
-   * Returns the Workflow object of this State.
-   *
-   * @return Workflow
-   *   Workflow object.
-   */
-  public function getWorkflow() {
-    if (!isset($this->workflow)) {
-      $this->workflow = Workflow::load($this->wid);
-    }
-    return $this->workflow;
-  }
-
-  /**
-   * @param Workflow $workflow
-   */
-  public function setWorkflow(Workflow $workflow) {
-    workflow_debug(__FILE__, __FUNCTION__, __LINE__);  // @todo D8-port: still test this snippet.
-
-    $this->wid = $workflow->id();
-    $this->workflow = $workflow;
-  }
-
   /**
    * Returns the Workflow object of this State.
    *
@@ -396,15 +362,16 @@ class WorkflowState extends ConfigEntityBase {
       // No workflow, no options ;-)
       return $transitions;
     }
+    // Load a User object, since we cannot add Roles to AccountInterface.
+    if (!$user = workflow_current_user($account)) {
+      // In some edge cases, no user is provided.
+      return $transitions;
+    }
 
     // @todo: Keep below code aligned between WorkflowState, ~Transition, ~TransitionListController
-
     /**
      * Get permissions of user, adding a Role to user, depending on situation.
      */
-    // Load a User object, since we cannot add Roles to AccountInterface.
-    /** @var \Drupal\user\UserInterface $user */
-    $user = workflow_current_user($account);
     // Determine if user is owner of the entity.
     $is_owner = WorkflowManager::isOwner($user, $entity);
 
@@ -423,6 +390,7 @@ class WorkflowState extends ConfigEntityBase {
      * Get the object and its permissions.
      */
     /** @var WorkflowConfigTransition[] $transitions */
+    /** @var Workflow $workflow */
     $transitions = $workflow->getTransitionsByStateId($this->id(), '');
 
     /**
@@ -518,6 +486,7 @@ class WorkflowState extends ConfigEntityBase {
       // We cannot use getTransitions, since there are no ConfigTransitions
       // from State with ID 0, and we do not want to repeat States.
       /** @var WorkflowState $state */
+      /** @var Workflow $workflow */
       foreach ($workflow->getStates() as $state) {
         $options[$state->id()] = html_entity_decode(t('@label', ['@label' => $state->label()]));
       }

+ 35 - 41
sites/all/modules/contrib/admin/workflow/src/Entity/WorkflowTransition.php

@@ -8,6 +8,7 @@ use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Language\Language;
 use Drupal\user\Entity\User;
 use Drupal\user\UserInterface;
+use Drupal\workflow\WorkflowTypeAttributeTrait;
 
 /**
  * Implements an actual, executed, Transition.
@@ -57,6 +58,11 @@ use Drupal\user\UserInterface;
  */
 class WorkflowTransition extends ContentEntityBase implements WorkflowTransitionInterface {
 
+  /*
+   * Add variables and get/set methods for Workflow property.
+   */
+  use WorkflowTypeAttributeTrait;
+
   /*
    * Transition data: are provided via baseFieldDefinitions().
    */
@@ -64,7 +70,6 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
   /*
    * Cache data.
    */
-  protected $workflow; // Use WorkflowTransition->getWorkflow() to fetch this.
   protected $entity = NULL; // Use WorkflowTransition->getTargetEntity() to fetch this.
   protected $user = NULL; // Use WorkflowTransition->getOwner() to fetch this.
 
@@ -116,17 +121,15 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
   public static function create(array $values = []) {
     if (is_array($values) && isset($values[0])) {
       $value = $values[0];
-      $values['wid'] = '';
-      $values['from_sid'] = '';
-      if (is_string($value) && $state = WorkflowState::load($value)) {
-        $values['wid'] = $state->getWorkflowId();
-        $values['from_sid'] = $state->id();
+      $state = NULL;
+      if (is_string($value)) {
+        $state = WorkflowState::load($value);
       }
       elseif (is_object($value) && $value instanceof WorkflowState) {
         $state = $value;
-        $values['wid'] = $state->getWorkflowId();
-        $values['from_sid'] = $state->id();
       }
+      $values['wid'] = $state ? $state->getWorkflowId() : '';
+      $values['from_sid'] = $state ? $state->id() : '';
     }
 
     // Add default values.
@@ -185,6 +188,10 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
   public function save() {
     // return parent::save();
 
+    // Set Target Entity to be used by Rules.
+    $reference = $this->get('entity_id')->first();
+    $reference->set('entity', $this->getTargetEntity());
+
     // Avoid custom actions for subclass WorkflowScheduledTransition.
     if ($this->isScheduled()) {
       return parent::save();
@@ -257,7 +264,7 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
     /** @var $query \Drupal\Core\Entity\Query\QueryInterface */
     $query = \Drupal::entityQuery($transition_type)
       ->condition('entity_type', $entity_type)
-      ->sort('timestamp', $sort) // 'DESC' || 'ASC'
+      ->sort('timestamp', $sort)// 'DESC' || 'ASC'
       ->addTag($transition_type);
     if (!empty($entity_ids)) {
       $query->condition('entity_id', $entity_ids, 'IN');
@@ -387,6 +394,17 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
     return $result;
   }
 
+  /**
+   * Determines if the State changes by this Transition.
+   * @return bool
+   */
+  public function hasStateChange() {
+    if ($this->from_sid->target_id == $this->to_sid->target_id) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -409,7 +427,7 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
 
     static $static_info = NULL;
 
-    if (isset($static_info[$entity_id][$field_name][$label])) {
+    if (isset($static_info[$entity_id][$field_name][$label]) && !$this->isEmpty()) {
       // Error: this Transition is already executed.
       // On the development machine, execute() is called twice, when
       // on an Edit Page, the entity has a scheduled transition, and
@@ -451,8 +469,7 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
     // @todo: move below code to $this->isAllowed().
     // If the state has changed, check the permissions.
     // No need to check if Comments or attached fields are filled.
-    $state_changed = ($from_sid != $to_sid);
-    if ($state_changed) {
+    if ($this->hasStateChange()) {
       // Make sure this transition is allowed by workflow module Admin UI.
       if (!$force) {
         $user->addRole(WORKFLOW_ROLE_AUTHOR_RID);
@@ -503,7 +520,7 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
         $this->save();
 
         // Register state change with watchdog.
-        if ($state_changed && !empty($this->getWorkflow()->options['watchdog_log'])) {
+        if ($this->hasStateChange() && !empty($this->getWorkflow()->options['watchdog_log'])) {
           if ($this->getEntityTypeId() == 'workflow_scheduled_transition') {
             $message = 'Scheduled state change of @entity_type_label %entity_label to %sid2 executed';
           }
@@ -597,31 +614,6 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getWorkflow() {
-    if (!$this->workflow && $wid = $this->getWorkflowId()) {
-      $this->workflow = Workflow::load($wid);
-    }
-    return $this->workflow;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getWorkflowId() {
-
-    if (!$this->wid->target_id && $state = $this->getFromState()) {
-      // Fallback.
-      $state = ($state) ? $state : $this->getToState();
-      $wid = ($state) ? $state->getWorkflowId() : '';
-
-      $this->set('wid', $wid);
-    }
-    return $this->wid->target_id;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -1074,12 +1066,14 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
     $user = $transition->getOwner();
     $user_name = ($user) ? $user->getAccountName() : 'unknown username';
     $t_string = $this->getEntityTypeId() . ' ' . $this->id() . ' for workflow_type <i>' . $this->getWorkflowId() . '</i> ' . ($function ? ("in function '$function'") : '');
-    $output[] = 'Entity  = ' . $this->getTargetEntityTypeId() . '/' . (($entity) ? ($entity->bundle() . '/' . $entity->id()) : '___/0') ;
+    $output[] = 'Entity  = ' . $this->getTargetEntityTypeId() . '/' . (($entity) ? ($entity->bundle() . '/' . $entity->id()) : '___/0');
     $output[] = 'Field   = ' . $transition->getFieldName();
     $output[] = 'From/To = ' . $transition->getFromSid() . ' > ' . $transition->getToSid() . ' @ ' . $time;
     $output[] = 'Comment = ' . $user_name . ' says: ' . $transition->getComment();
-    $output[] = 'Forced  = ' . ($transition->isForced() ? 'yes' : 'no') .'; ' . 'Scheduled = ' . ($transition->isScheduled() ? 'yes' : 'no');
-    if (function_exists('dpm')) { dpm($output, $t_string); }
+    $output[] = 'Forced  = ' . ($transition->isForced() ? 'yes' : 'no') . '; ' . 'Scheduled = ' . ($transition->isScheduled() ? 'yes' : 'no');
+    if (function_exists('dpm')) { // in Workflow->dpm().
+      dpm($output, $t_string);    // in Workflow->dpm().
+    }                             // in Workflow->dpm().
   }
 
 }

+ 1 - 3
sites/all/modules/contrib/admin/workflow/src/Form/WorkflowTransitionForm.php

@@ -45,13 +45,11 @@ class WorkflowTransitionForm extends ContentEntityForm {
     // $entity_type = $transition->getTargetEntityTypeId();
     // $entity_id = $transition->getTargetEntityId();;
 
+    $suffix = 'form';
     // Emulate nodeForm convention.
     if ($transition->id()) {
       $suffix = 'edit_form';
     }
-    else {
-      $suffix = 'form';
-    }
     $form_id = implode('_', ['workflow_transition', $field_name, $suffix]);
     $form_id = Html::getUniqueId($form_id);
 

+ 1 - 1
sites/all/modules/contrib/admin/workflow/src/Plugin/Action/WorkflowNodeGivenStateAction.php

@@ -73,7 +73,7 @@ class WorkflowNodeGivenStateAction extends WorkflowStateActionBase {
     }
 
     // Fire the transition.
-    workflow_execute_transition($transition, $force);
+    workflow_execute_transition($transition, $force = FALSE);
   }
 
 }

+ 6 - 7
sites/all/modules/contrib/admin/workflow/src/Plugin/Action/WorkflowStateActionBase.php

@@ -109,7 +109,6 @@ abstract class WorkflowStateActionBase extends ConfigurableActionBase implements
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $form = [];
 
     // If we are on admin/config/system/actions and use CREATE AN ADVANCED ACTION
     // Then $context only contains:
@@ -126,18 +125,18 @@ abstract class WorkflowStateActionBase extends ConfigurableActionBase implements
     $wids = workflow_get_workflow_names();
 
     if (empty($field_name) && count($wids) > 1) {
-      drupal_set_message('You have more then one workflow in the system. Please first select the field name
+      drupal_set_message('You have multiple workflows in the system. Please first select the field name
           and save the form. Then, revisit the form to set the correct state value.', 'warning');
     }
-    if (empty($field_name)) {
-      $wid = count($wids) ? array_keys($wids)[0] : '';
-    }
-    else {
+
+    $wid = count($wids) ? array_keys($wids)[0] : '';
+    if (!empty($field_name)) {
       $fields = _workflow_info_fields($entity = NULL, $entity_type = '', $entity_bundle = '', $field_name);
       $wid = count($fields) ? reset($fields)->getSetting('workflow_type') : '';
     }
 
     // Get the common Workflow, or create a dummy Workflow.
+    /** @var Workflow $workflow */
     $workflow = $wid ? Workflow::load($wid) : Workflow::create(['id' => 'dummy_action', 'label' => 'dummy_action']);
     $current_state = $workflow->getCreationState();
 
@@ -183,7 +182,7 @@ abstract class WorkflowStateActionBase extends ConfigurableActionBase implements
       \Drupal::time()->getRequestTime(),
       $comment = $config['comment'],
       $force = $config['force']
-  );
+    );
 
     // Add the WorkflowTransitionForm to the page. @todo
 

+ 75 - 0
sites/all/modules/contrib/admin/workflow/src/Plugin/Derivative/WorkflowLocalTask.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\workflow\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides local task definitions for all entity bundles.
+ *
+ * @see \Drupal\workflow\Controller\EntityDebugController
+ * @see \Drupal\workflow\Routing\RouteSubscriber
+ */
+class WorkflowLocalTask extends DeriverBase implements ContainerDeriverInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Creates an WorkflowLocalTask object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The translation manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $this->derivatives = [];
+
+    $field_list = workflow_get_workflow_fields_by_entity_type();
+    foreach ($field_list as $entity_type_id => $fields) {
+      $this->derivatives["entity.$entity_type_id.workflow_history"] = [
+        'route_name' => "entity.$entity_type_id.workflow_history",
+        'title' => $this->t('Workflow'),
+        'base_route' => "entity.$entity_type_id.canonical",
+        'weight' => 100,
+      ];
+    }
+
+    foreach ($this->derivatives as &$entry) {
+      $entry += $base_plugin_definition;
+    }
+
+    return $this->derivatives;
+  }
+
+}

+ 0 - 1
sites/all/modules/contrib/admin/workflow/src/Plugin/Field/FieldType/WorkflowItem.php

@@ -156,7 +156,6 @@ class WorkflowItem extends ListItemBase {
 
     return [
       'workflow_type' => '',
-      'allowed_values_function' => 'workflow_state_allowed_values',
     ] + parent::defaultStorageSettings();
   }
 

+ 1 - 1
sites/all/modules/contrib/admin/workflow/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php

@@ -78,7 +78,7 @@ class WorkflowDefaultWidget extends WidgetBase {
     $field_storage = $field_config->getFieldStorageDefinition();
 
     $entity = $item->getEntity();
-    $field_name = $field_storage->get('field_name');
+    $field_name = $field_storage->getName();
 
     // Create a transition, to pass to the form. No need to use setValues().
     $from_sid = workflow_node_current_state($entity, $field_name);

+ 2 - 3
sites/all/modules/contrib/admin/workflow/src/Plugin/Validation/Constraint/WorkflowFieldConstraintValidator.php

@@ -3,10 +3,8 @@
 namespace Drupal\workflow\Plugin\Validation\Constraint;
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\user\UserStorageInterface;
-use Drupal\workflow\Entity\WorkflowTransitionInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
@@ -54,6 +52,7 @@ class WorkflowFieldConstraintValidator extends ConstraintValidator implements Co
    */
   public function validate($entity, Constraint $constraint) {
     // Workflow field name on CommentForm has special requirements.
+    /** @var FieldStorageConfig $field_storage */
     $field_storage = $entity->getFieldDefinition()->getFieldStorageDefinition();
     if ($field_storage->getTargetEntityTypeId() == 'comment') {
       if (!$this->isValidFieldname($field_storage)) {
@@ -76,7 +75,7 @@ class WorkflowFieldConstraintValidator extends ConstraintValidator implements Co
       return TRUE;
     }
 
-    $field_name = $field_storage->get('field_name');
+    $field_name = $field_storage->getName();
 
     // Check if the 'comment' field name exists on the 'commented' entity type.
     // @todo: Still not waterproof. You could have a field on a non-relevant entity_type.

+ 136 - 0
sites/all/modules/contrib/admin/workflow/src/Routing/RouteSubscriber.php

@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\workflow\Routing;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Subscriber for Workflow routes.
+ *
+ * @see \Drupal\workflow\Plugin\Derivative\WorkflowLocalTask
+ */
+class RouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new RouteSubscriber object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_manager) {
+    $this->entityTypeManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+
+    $field_list = workflow_get_workflow_fields_by_entity_type();
+    foreach ($field_list as $entityTypeId => $fields) {
+
+      /*
+       * @todo Create a Entity workflow field list page and a route
+       * that redirect to the page when a entity have more than
+       * one workflow fields.
+       */
+
+      // Only 1 field. Workflow is redirect to workflow/{field_name}.
+      if (count($fields) < 2) {
+        $path = "/$entityTypeId/{{$entityTypeId}}/workflow";
+        $route = $this->getEntityLoadRoute($entityTypeId, $path);
+        $collection->add("entity.$entityTypeId.workflow_history", $route);
+      }
+
+      // Generate one route for each workflow field.
+      foreach ($fields as $field_name => $field) {
+        $path = "/$entityTypeId/{{$entityTypeId}}/workflow/$field_name";
+        $route = $this->getEntityLoadRoute($entityTypeId, $path);
+        $collection->add("entity.$entityTypeId.workflow_history.$field_name", $route);
+      }
+    }
+  }
+
+  /**
+   * Gets the entity load route.
+   *
+   * @param string $entityTypeId
+   *   The entity type id.
+   * @param string $path
+   *   The Path of the route.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function getEntityLoadRoute($entityTypeId, $path) {
+
+    /*
+     * @todo Create the Route for taxonomy term like
+     * '/taxonomy/term/{taxonomy_term}/workflow/{field_name}'
+     *
+     */
+    $route = new Route(
+      $path,
+      [
+        '_controller' => '\Drupal\workflow\Controller\WorkflowTransitionListController::historyOverview',
+        '_title' => 'Workflow history',
+      ],
+      [
+        '_custom_access' => '\Drupal\workflow\Access\WorkflowHistoryAccess::access',
+      ],
+      [
+        '_admin_route' => TRUE,
+        '_workflow_entity_type_id' => $entityTypeId, // @todo: remove
+        'parameters' => [
+          $entityTypeId => ['type' => 'entity:' . $entityTypeId],
+        ],
+      ]
+    );
+
+    return $route;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = parent::getSubscribedEvents();
+    $events[RoutingEvents::ALTER] = ['onAlterRoutes', 100];
+    return $events;
+  }
+
+  /**
+   * Get all field of type workflow.
+   *
+   * @return array
+   *   Return all workflow fields.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function listWorkflowFields() {
+    $fieldType = 'workflow';
+    $fieldStorageConfigs = $this->entityTypeManager->getStorage('field_storage_config')->loadByProperties(['type' => $fieldType]);
+    if (!$fieldStorageConfigs) {
+      return [];
+    }
+
+    $availableItems = [];
+    foreach ($fieldStorageConfigs as $fieldStorage) {
+      $availableItems[] = $fieldStorage;
+    }
+
+    return $availableItems;
+  }
+
+}

+ 4 - 3
sites/all/modules/contrib/admin/workflow/src/WorkflowTransitionListBuilder.php

@@ -3,8 +3,8 @@
 namespace Drupal\workflow;
 
 use Drupal\Component\Utility\Html;
-use Drupal\Core\Entity\EntityListBuilder;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListBuilder;
 use Drupal\workflow\Entity\WorkflowTransition;
 
 /**
@@ -55,6 +55,7 @@ class WorkflowTransitionListBuilder extends EntityListBuilder {
     $field_name = workflow_url_get_field_name();
 
     // @todo D8-port: document $limit. Should be used in pager, not in load().
+    // N.B. Using the provided default History view is recommended.
     $this->limit = \Drupal::config('workflow.settings')->get('workflow_states_per_page');
     $limit = $this->limit;
     // Get Transitions with highest timestamp first.
@@ -202,7 +203,7 @@ class WorkflowTransitionListBuilder extends EntityListBuilder {
           'class' => ['footer-class'],
           'data' => [
             [
-              'data' => WORKFLOW_MARK_STATE_IS_DELETED . ' ' . t('State is no longer available.'),
+              'data' => self::WORKFLOW_MARK_STATE_IS_DELETED . ' ' . t('State is no longer available.'),
               'colspan' => count($build['table']['#header']),
             ],
           ],
@@ -210,7 +211,7 @@ class WorkflowTransitionListBuilder extends EntityListBuilder {
       ];
        */
       $build['workflow_footer'] = [
-        '#markup' => WORKFLOW_MARK_STATE_IS_DELETED . ' ' . t('State is no longer available.'),
+        '#markup' => self::WORKFLOW_MARK_STATE_IS_DELETED . ' ' . t('State is no longer available.'),
         '#weight' => 500, // @todo Make this better.
       ];
     }

+ 103 - 0
sites/all/modules/contrib/admin/workflow/src/WorkflowTypeAttributeTrait.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\workflow;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\workflow\Entity\Workflow;
+
+/**
+ * Wrapper methods for Workflow* objects
+ *
+ * Using this trait will add getWorkflow(), getWorkflowID() and setWorkflow()
+ * methods to the class.
+ *
+ * @ingroup workflow
+ */
+trait WorkflowTypeAttributeTrait {
+
+  /**
+   * The machine_name of the attached Workflow.
+   *
+   * @var string
+   */
+  protected $wid = '';
+
+  /**
+   * The attached Workflow.
+   * It must explicitly be defined, and not be public, to avoid errors
+   * when exporting with json_encode().
+   *
+   * @var Workflow
+   */
+  protected $workflow = NULL;
+
+  /**
+   * @param Workflow $workflow
+   */
+  public function setWorkflow(Workflow $workflow) {
+    $this->wid = '';
+    $this->workflow = NULL;
+    if ($workflow) {
+      $this->wid = $workflow->id();
+      $this->workflow = $workflow;
+    }
+  }
+
+  /**
+   * Returns the Workflow object of this object.
+   *
+   * @return Workflow
+   *   Workflow object.
+   */
+  public function getWorkflow() {
+    if (!empty($this->workflow)) {
+      return $this->workflow;
+    }
+
+    /** @noinspection PhpAssignmentInConditionInspection */
+    if ($wid = $this->getWorkflowId()) {
+      $this->workflow = Workflow::load($wid);
+    }
+    return $this->workflow;
+  }
+
+  /**
+   * Sets the Workflow ID of this object.
+   *
+   * @return object
+   */
+  public function setWorkflowId($wid) {
+    $this->wid = $wid;
+    $this->workflow = NULL;
+    return $this;
+  }
+
+  /**
+   * Returns the Workflow ID of this object.
+   *
+   * @return string
+   *   Workflow Id.
+   */
+  public function getWorkflowId() {
+    /** @var ContentEntityBase $this */
+    if (!empty($this->wid)) {
+      return $this->wid;
+    }
+
+    $value = $this->get('wid');
+    if (is_string($value)) {
+      $this->wid = $value;
+    }
+    elseif (is_object($value)) {
+      $wid = isset($value->getValue()[0]['target_id']) ? $value->getValue()[0]['target_id'] : '';
+      // or: $this->set('wid', $wid);
+      $this->wid = $wid; // in WorkflowTransition.
+    }
+    else {
+      workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
+    }
+
+    return $this->wid;
+  }
+
+}

+ 16 - 0
sites/all/modules/contrib/admin/workflow/workflow.field.inc

@@ -95,6 +95,7 @@ function workflow_state_formatter($entity, $field_name, $current_sid = '') {
   // If user creates a node, and only 1 option is available, the formatter
   // is shown with key, not value, because creation state does not count.
   // In this case, hide the formatter.
+  /** @var \Drupal\workflow\Entity\WorkflowState $state */
   $state = \Drupal\workflow\Entity\WorkflowState::load($current_sid);
   if ($state->isCreationState()) {
     return $element;
@@ -117,3 +118,18 @@ function workflow_state_formatter($entity, $field_name, $current_sid = '') {
 
   return $element;
 }
+
+/**
+ * This function is a residue for installations that have v8.x-1.0
+ * The setting for options_allowed_values is still in the Field confuguration
+ * Saving the field data again does not remove the settings.
+ * So, we copy code from options_allowed_values().
+ *
+ * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
+ *
+ * @return mixed
+ */
+function workflow_state_allowed_values(\Drupal\Core\Field\FieldStorageDefinitionInterface $definition) {
+  $values = $definition->getSetting('allowed_values');
+  return $values;
+}

+ 1 - 1
sites/all/modules/contrib/admin/workflow/workflow.form.inc

@@ -190,7 +190,7 @@ function _workflow_transition_form_get_action_buttons(array $form, array $workfl
       if (isset($form['#form_id']) && substr($form['#form_id'], 0, 24) == 'workflow_transition_form') {
         // Hide same-state-button on the transition-form (that is:
         // view page or workflow history tab) if there is nothing to do.
-        // However, a Transition may be fieldable.
+        // However, a Transition may be fieldable (have attached fields).
         if ($form['comment']['#access'] == FALSE) {
           $workflow_submit_action['#access'] = FALSE;
         }

+ 3 - 3
sites/all/modules/contrib/admin/workflow/workflow.info.yml

@@ -10,8 +10,8 @@ dependencies:
   - options
   - user
 
-# Information added by Drupal.org packaging script on 2017-12-17
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2018-04-29
+version: '8.x-1.1'
 core: '8.x'
 project: 'workflow'
-datestamp: 1513552400
+datestamp: 1525039388

+ 0 - 7
sites/all/modules/contrib/admin/workflow/workflow.install

@@ -18,13 +18,6 @@ function workflow_install() {
   drupal_set_message($message);
 }
 
-/**
- * Implements hook_uninstall().
- */
-function workflow_uninstall() {
-//  \Drupal::config('workflow.settings')->clear('workflow_states_per_page')->save(); // @FIXME
-}
-
 /**
  * Implements hook_requirements().
  *

+ 3 - 12
sites/all/modules/contrib/admin/workflow/workflow.links.task.yml

@@ -18,15 +18,6 @@ entity.workflow_type.delete_form:
   title:  'Delete'
   weight: 10
 
-# @todo D8-port: make Workflow History tab happen for every entity_type.
-# @see workflow.routing.yml, workflow.links.task.yml, WorkflowTransitionListController.
-entity.node.workflow_history:
-  route_name: entity.node.workflow_history
-  base_route: entity.node.canonical
-  title: 'Workflow'
-  weight: 30
-entity.taxonomy_term.edit_form:
-  route_name: entity.taxonomy_term.workflow_history
-  base_route: entity.taxonomy_term.canonical
-  title: 'Workflow'
-  weight: 30
+workflow.entities:
+  deriver: 'Drupal\workflow\Plugin\Derivative\WorkflowLocalTask'
+  weight: 100

+ 63 - 55
sites/all/modules/contrib/admin/workflow/workflow.module

@@ -176,9 +176,9 @@ function workflow_entity_delete(\Drupal\Core\Entity\EntityInterface $entity) {
   elseif (!WorkflowManager::isWorkflowEntityType($entity->getEntityTypeId())) {
     // A 'normal' entity is deleted.
     foreach ($fields = _workflow_info_fields($entity) as $field_id => $field_storage) {
-      $entity_type = $field_storage->getTargetEntityTypeId();
       $entity_id = $entity->id();
-      $field_name = $field_storage->get('field_name');
+      $entity_type = $field_storage->getTargetEntityTypeId();
+      $field_name = $field_storage->getName();
       /** @var $transition Drupal\workflow\Entity\WorkflowTransitionInterface */
       foreach (WorkflowScheduledTransition::loadMultipleByProperties($entity_type, [$entity_id], [], $field_name) as $transition) {
         $transition->delete();
@@ -347,8 +347,8 @@ function workflow_get_workflow_names($required = TRUE) {
 function workflow_get_workflow_field_names($entity = NULL, $entity_type = '', $entity_bundle = '', $field_name = '') {
   $result = [];
   foreach (_workflow_info_fields($entity, $entity_type, $entity_bundle, $field_name) as $definition) {
-    $field_name = $definition->getName();
-    $result[$field_name] = $definition->getName();
+    $field_name2 = $definition->getName();
+    $result[$field_name2] = $field_name2;
   }
   return $result;
 }
@@ -397,10 +397,8 @@ function workflow_get_field_name(\Drupal\Core\Entity\EntityInterface $entity, $f
   if ($field_name) {
     return $field_name;
   }
-
-  $fields = _workflow_info_fields($entity);
-  $field = reset($fields);
-  $field_name = $field->getName();
+  $fields = workflow_get_workflow_field_names($entity);
+  $field_name = reset($fields);
   return $field_name;
 }
 
@@ -428,8 +426,6 @@ function workflow_current_user(\Drupal\Core\Session\AccountInterface $account =
  * @param string $field_name
  *
  * @return string $current_sid
- *
- * @deprecated : use WorkflowManager::getCurrentStateId()
  */
 function workflow_node_current_state(\Drupal\Core\Entity\EntityInterface $entity, $field_name = '') {
   return WorkflowManager::getCurrentStateId($entity, $field_name);
@@ -442,8 +438,6 @@ function workflow_node_current_state(\Drupal\Core\Entity\EntityInterface $entity
  * @param string $field_name
  *
  * @return string $previous_sid
- *
- * @deprecated : use WorkflowManager::getPreviousStateId()
  */
 function workflow_node_previous_state(\Drupal\Core\Entity\EntityInterface $entity, $field_name = '') {
   return WorkflowManager::getPreviousStateId($entity, $field_name);
@@ -486,6 +480,10 @@ function workflow_get_workflows_by_type($entity_bundle, $entity_type) {
   return $map[$entity_type][$entity_bundle];
 }
 
+function workflow_get_workflow_fields_by_entity_type() {
+  return \Drupal::service('entity_field.manager')->getFieldMapByFieldType('workflow');
+}
+
 /**
  * Gets the workflow field names, if not known already.
  *
@@ -522,7 +520,24 @@ function _workflow_info_fields($entity = NULL, $entity_type = '', $entity_bundle
             // Do not use the field_name as ID, but the unique <entity_type>.<field_name>
             // since you cannot share the same field on multiple entity_types (unlike D7).
             $field_config = \Drupal\field\Entity\FieldStorageConfig::loadByName($e_type, $f_name);
-            $field_info[$field_config->id()] = $field_config;
+            if ($field_config) {
+              $field_info[$field_config->id()] = $field_config;
+            }
+            else {
+              // The field is a base/extra field not a configurable Field via Field UI.
+
+              // Refetch the field definitions, with extra data.
+              $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions($e_type, $entity_bundle);
+              // @todo ?? Loop over bundles?
+              /** @var \Drupal\Core\Field\BaseFieldDefinition $field_config */
+              $field_config = $field_definitions[$f_name];
+              if ($field_config) {
+                $field_info[$field_config->getUniqueStorageIdentifier()] = $field_config;
+              }
+              else {
+                // @todo ?? Loop over bundles?
+              }
+            }
           }
         }
       }
@@ -545,36 +560,27 @@ function workflow_url_get_entity(\Drupal\Core\Entity\EntityInterface $entity = N
     return $entity;
   }
 
-  $route_match = \Drupal::routeMatch();
-
-  // @todo: get entity for any route.
-  // On node pages, we'd get an object.
-  $entity = $route_match->getParameter('node');
-  if ($entity && is_object($entity)) {
-    return $entity;
-  }
-  if ($entity && !is_object($entity)) {
-    // On workflow tab, we'd get an id.
-    $entity = \Drupal\node\Entity\Node::load($entity);
-    return $entity;
+  $entities = [];
+  foreach (\Drupal::routeMatch()->getParameters() as $param) {
+    if ($param instanceof \Drupal\Core\Entity\EntityInterface) {
+      $entities[] = $param;
+    }
   }
+  $value = reset($entities);
 
-  // It was not a Node, try a Term.
-  // On term pages, we get objects, or id's.
-  $entity = $route_match->getParameter('taxonomy_term');
-  if ($entity && is_object($entity)) {
-    return $entity;
+  if ($value && is_object($value)) {
+    return $value;
   }
-  elseif ($entity && !is_object($entity)) {
-    $entity = \Drupal\taxonomy\Entity\Term::load($entity);
+
+  if ($value && !is_object($value)) {
+    // On workflow tab, we'd get an id.
+    // This is an indicator that the route is mal-configured.
+    workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'route declaration is not optimal.');
+    $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($value);
     return $entity;
   }
 
-  if (!$entity) {
-    // We may be on a entity add page/
-    // Or we may be on a page of some unknown entity.
-  }
-  return $entity;
+  return $value;
 }
 
 /**
@@ -585,10 +591,7 @@ function workflow_url_get_entity(\Drupal\Core\Entity\EntityInterface $entity = N
  * @return string|null $field_name
  */
 function workflow_url_get_field_name() {
-  /** @var $route_match \Drupal\Core\Routing\RouteMatchInterface */
-  $route_match = \Drupal::routeMatch();
-  $field_name = $route_match->getParameter('field_name');
-  return $field_name;
+  return workflow_url_get_parameter('field_name');
 }
 
 /**
@@ -609,32 +612,37 @@ function workflow_url_get_operation() {
 }
 
 /**
- * Helper function to determine Workflow from Workflow UI URL.
+ * Helper function to get arbitrary parameter from a route.
  *
- * @param string $url
+ * @param $parameter
+ *   The requested parameter.
+ * @return string $field_name
+ */
+function workflow_url_get_parameter($parameter) {
+  return \Drupal::routeMatch()->getParameter($parameter);
+  // return \Drupal::request()->get($parameter);
+}
+
+/**
+ * Helper function to determine Workflow from Workflow UI URL.
  * @return Workflow
  */
-function workflow_url_get_workflow($url = '' ) {
+function workflow_url_get_workflow() {
   /** @var $workflows \Drupal\workflow\Entity\Workflow[] */
   static $workflows = [];
 
-  // For some reason, $_SERVER is not allowed as default.
-  $url = ($url == '') ? $_SERVER['REQUEST_URI'] : $url;
+  $wid = workflow_url_get_parameter('workflow_type');
+  if (is_object($wid)) { // $wid is a Workflow object.
+    return $wid;
+  }
 
-  // The URL may have prefixes: /
-  // ex.1: /en/admin/config/workflow/workflow/MY_WORKFLOW/states
-  // ex.2: /admin/config/workflow/workflow/MY_WORKFLOW/states
-  $base_url = '/config/workflow/workflow/';
-  $string = substr($url, strpos($url, $base_url) + strlen($base_url));
-  $wid = explode('/', $string)[0];
+  // $wid is a string
   if (!isset($workflows[$wid])) {
-    $workflows[$wid] = Workflow::load($wid);
+    $workflows[$wid] = $wid ? Workflow::load($wid) : NULL;
   }
-
   return $workflows[$wid];
 }
 
-
 /**
  * Helper function to determine the title of the page.
  *

+ 0 - 33
sites/all/modules/contrib/admin/workflow/workflow.routing.yml

@@ -109,36 +109,3 @@ entity.workflow_scheduled_transition.delete_form:
     _admin_route: TRUE
   requirements:
     _entity_access: 'workflow_transition.delete'
-
-### The Workflow Transition History List (Tab)
-# @todo D8-port: make Workflow History tab happen for every entity_type.
-# @see workflow.routing.yml, workflow.links.task.yml, WorkflowTransitionListController.
-# A route for showing the Workflow history tab.
-entity.node.workflow_history:
-  path: '/node/{node}/workflow/{field_name}'
-  defaults:
-    _title: 'Workflow history'
-    _controller: '\Drupal\workflow\Controller\WorkflowTransitionListController::historyOverview'
-    field_name: ~
-  requirements:
-    _custom_access: '\Drupal\workflow\Controller\WorkflowTransitionListController::historyAccess'
-    _module_dependencies: 'node'
-  options:
-    _admin_route: TRUE
-    parameters:
-      entity:
-        type: entity:{entity_type}
-
-entity.taxonomy_term.workflow_history:
-  path: '/taxonomy/term/{taxonomy_term}/workflow/{field_name}'
-  defaults:
-    _title: 'Workflow history'
-    _controller: '\Drupal\workflow\Controller\WorkflowTransitionListController::historyOverview'
-    field_name: ~
-  requirements:
-    _custom_access: '\Drupal\workflow\Controller\WorkflowTransitionListController::historyAccess'
-    _module_dependencies: 'taxonomy'
-  options:
-    parameters:
-      entity:
-        type: entity:{entity_type}

+ 5 - 5
sites/all/modules/contrib/admin/workflow/workflow.services.yml

@@ -3,8 +3,8 @@ services:
     class: Drupal\workflow\Entity\WorkflowManager
     arguments: ['@entity_type.manager', '@entity.query', '@config.factory', '@string_translation', '@module_handler', '@current_user']
 
-#  workflow.route_subscriber:
-#    class: Drupal\workflow\Routing\RouteSubscriber
-#    arguments: ['@entity.manager']
-#    tags:
-#      - { name: event_subscriber }
+  workflow.route_subscriber:
+    class: Drupal\workflow\Routing\RouteSubscriber
+    arguments: ['@entity.manager']
+    tags:
+      - { name: event_subscriber }

+ 1 - 4
sites/all/modules/contrib/dev/redis/.travis.yml

@@ -12,12 +12,9 @@ php:
   - 5.5
   - 5.6
   - 7
-  - hhvm
 
 matrix:
   fast_finish: true
-  allow_failures:
-    - php: hhvm
 
 env:
   global:
@@ -63,7 +60,7 @@ env:
     - DRUPAL_TI_BEHAT_BROWSER="firefox"
 
     # Use Drupal 8.3.x to run tests.
-    - DRUPAL_TI_CORE_BRANCH="8.3.x"
+    - DRUPAL_TI_CORE_BRANCH="8.5.x"
 
     # PHPUnit specific commandline arguments.
     - DRUPAL_TI_PHPUNIT_ARGS="--verbose --debug"

+ 3 - 3
sites/all/modules/contrib/dev/redis/redis.info.yml

@@ -5,8 +5,8 @@ type: module
 # core: 8.x
 configure: redis.admin_display
 
-# Information added by Drupal.org packaging script on 2017-09-14
-version: '8.x-1.0-rc2'
+# Information added by Drupal.org packaging script on 2018-05-30
+version: '8.x-1.0'
 core: '8.x'
 project: 'redis'
-datestamp: 1505390051
+datestamp: 1527699489

+ 2 - 1
sites/all/modules/contrib/dev/redis/src/Cache/CacheBase.php

@@ -3,6 +3,7 @@
 namespace Drupal\redis\Cache;
 
 use \DateInterval;
+use Drupal\Component\Assertion\Inspector;
 use Drupal\Component\Serialization\SerializationInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
@@ -297,7 +298,7 @@ abstract class CacheBase implements CacheBackendInterface {
     // Always add a cache tag for the current bin, so that we can use that for
     // invalidateAll().
     $tags[] = $this->getTagForBin();
-    assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache Tags must be strings.');
+    assert(Inspector::assertAllStrings($tags), 'Cache Tags must be strings.');
     $hash = [
       'cid' => $cid,
       'created' => round(microtime(TRUE), 3),

+ 2 - 2
sites/all/modules/contrib/dev/redis/src/Lock/Predis.php

@@ -84,8 +84,8 @@ class Predis extends LockBackendAbstract {
       // succeed if the key does not exist yet.
       $result = $this->client->set($key, $id, 'nx', 'px', (int) ($timeout * 1000));
 
-      // If the result is FALSE, we failed to acquire the lock.
-      if (FALSE === $result) {
+      // If the result is FALSE or NULL, we failed to acquire the lock.
+      if (FALSE === $result || NULL === $result) {
         return FALSE;
       }
 

+ 26 - 0
sites/all/modules/contrib/dev/redis/src/PersistentLock/Predis.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\redis\PersistentLock;
+
+use Drupal\redis\ClientFactory;
+
+/**
+ * Predis persistent lock backend
+ */
+class Predis extends \Drupal\redis\Lock\Predis {
+
+  /**
+   * Creates a Predis persistent lock backend.
+   */
+  public function __construct(ClientFactory $factory) {
+    // Do not call the parent constructor to avoid registering a shutdown
+    // function that releases all the locks at the end of a request.
+    $this->client = $factory->getClient();
+    // Set the lockId to a fixed string to make the lock ID the same across
+    // multiple requests. The lock ID is used as a page token to relate all the
+    // locks set during a request to each other.
+    // @see \Drupal\Core\Lock\LockBackendInterface::getLockId()
+    $this->lockId = 'persistent';
+  }
+
+}

+ 1 - 1
sites/all/modules/contrib/dev/redis/tests/src/Functional/Lock/RedisLockFunctionalTest.php

@@ -41,7 +41,7 @@ class RedisLockFunctionalTest extends LockFunctionalTest {
     file_put_contents($filename, $contents);
     $settings = Settings::getAll();
     $settings['container_yamls'][] = 'modules/redis/example.services.yml';
-    $settings['redis.connection']['interface'] = '\'' .  $redis_interface . '\'';
+    $settings['redis.connection']['interface'] = $redis_interface;
     new Settings($settings);
     OpCodeCache::invalidate(DRUPAL_ROOT . '/' . $filename);
 

+ 4 - 5
sites/all/modules/contrib/dev/redis/tests/src/Functional/WebTest.php

@@ -3,7 +3,6 @@
 namespace Drupal\Tests\redis\Functional;
 
 use Drupal\Component\Utility\OpCodeCache;
-use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Site\Settings;
 use Drupal\field_ui\Tests\FieldUiTestTrait;
@@ -142,10 +141,10 @@ class WebTest extends BrowserTestBase {
     // Create a node type with a field.
     $edit = [
       'name' => $this->randomString(),
-      'type' => $node_type = Unicode::strtolower($this->randomMachineName()),
+      'type' => $node_type = mb_strtolower($this->randomMachineName()),
     ];
     $this->drupalPostForm('admin/structure/types/add', $edit, t('Save and manage fields'));
-    $field_name = Unicode::strtolower($this->randomMachineName());
+    $field_name = mb_strtolower($this->randomMachineName());
     $this->fieldUIAddNewField('admin/structure/types/manage/' . $node_type, $field_name, NULL, 'text');
 
     // Create a node, check display, edit, verify that it has been updated.
@@ -154,7 +153,7 @@ class WebTest extends BrowserTestBase {
       'body[0][value]' => $this->randomMachineName(),
       'field_' . $field_name . '[0][value]' => $this->randomMachineName(),
     ];
-    $this->drupalPostForm('node/add/' . $node_type, $edit, t('Save and publish'));
+    $this->drupalPostForm('node/add/' . $node_type, $edit, t('Save'));
 
     // Test the output as anonymous user.
     $this->drupalLogout();
@@ -169,7 +168,7 @@ class WebTest extends BrowserTestBase {
     $update = [
       'title[0][value]' => $this->randomMachineName(),
     ];
-    $this->drupalPostForm(NULL, $update, t('Save and keep published'));
+    $this->drupalPostForm(NULL, $update, t('Save'));
     $this->assertSession()->responseContains($update['title[0][value]']);
     $this->drupalGet('node');
     $this->assertSession()->responseContains($update['title[0][value]']);

+ 3 - 3
sites/all/modules/contrib/dev/redis/tests/src/Kernel/RedisQueueTest.php

@@ -39,7 +39,7 @@ class RedisQueueTest extends CoreQueueTest {
     $queue2 = new $class_name($this->randomMachineName(), $settings, $client_factory->getClient());
     $queue2->createQueue();
 
-    $this->queueTest($queue1, $queue2);
+    $this->runQueueTest($queue1, $queue2);
     $queue1->deleteQueue();
     $queue2->deleteQueue();
 
@@ -53,7 +53,7 @@ class RedisQueueTest extends CoreQueueTest {
     $queue2 = new $class_name($this->randomMachineName(), $settings, $client_factory->getClient());
     $queue2->createQueue();
 
-    $this->queueTest($queue1, $queue2);
+    $this->runQueueTest($queue1, $queue2);
   }
 
   /**
@@ -74,7 +74,7 @@ class RedisQueueTest extends CoreQueueTest {
     $queue2 = new $class_name($this->randomMachineName(), $settings, $client_factory->getClient());
     $queue2->createQueue();
 
-    $this->queueTest($queue1, $queue2);
+    $this->runQueueTest($queue1, $queue2);
   }
 
   /**

+ 3 - 3
sites/all/modules/contrib/fields/addtoany/addtoany.info.yml

@@ -7,8 +7,8 @@ dependencies:
   - drupal:node
 configure: addtoany.admin_settings
 
-# Information added by Drupal.org packaging script on 2018-03-07
-version: '8.x-1.9'
+# Information added by Drupal.org packaging script on 2018-07-03
+version: '8.x-1.10'
 core: '8.x'
 project: 'addtoany'
-datestamp: 1520385667
+datestamp: 1530656924

+ 35 - 25
sites/all/modules/contrib/fields/addtoany/addtoany.module

@@ -11,6 +11,7 @@ use Drupal\Core\Render\Markup;
 use Drupal\Core\Url;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\node\Entity\Node;
+use Drupal\addtoany\Form\AddToAnySettingsForm;
 
 /**
  * Implements hook_theme().
@@ -58,20 +59,25 @@ function addtoany_theme_suggestions_addtoany_standard(array $variables) {
  */
 function addtoany_entity_extra_field_info() {
   $extra = [];
-  $types = ['node', 'comment', 'media'];
+  $entityTypes = AddToAnySettingsForm::getContentEntities();
+  $config = Drupal::config('addtoany.settings');
 
   // Allow modules to alter the entity types.
-  \Drupal::moduleHandler()->alter('addtoany_entity_types', $types);
-
-  foreach ($types as $type) {
-    $bundles = Drupal::service('entity_type.bundle.info')->getBundleInfo($type);
-    foreach ($bundles as $bundle => $bundle_data) {
-      $extra[$type][$bundle]['display']['addtoany'] = [
-        'label' => t('AddToAny'),
-        'description' => t('Share buttons by AddToAny'),
-        'weight' => 5,
-        'visible' => FALSE,
-      ];
+  \Drupal::moduleHandler()->alter('addtoany_entity_types', $entityTypes);
+
+  foreach ($entityTypes as $type) {
+    $entityTypeId = $type->id();
+    $isAllowed = $config->get("entities.{$entityTypeId}");
+    if ($isAllowed) {
+      $bundles = Drupal::service('entity_type.bundle.info')->getBundleInfo($entityTypeId);
+      foreach ($bundles as $bundle => $bundle_data) {
+        $extra[$entityTypeId][$bundle]['display']['addtoany'] = [
+          'label' => t('AddToAny'),
+          'description' => t('Share buttons by AddToAny'),
+          'weight' => 5,
+          'visible' => FALSE,
+        ];
+      }
     }
   }
 
@@ -83,19 +89,23 @@ function addtoany_entity_extra_field_info() {
  */
 function addtoany_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
   if ($display->getComponent('addtoany')) {
-    $data = addtoany_create_entity_data($entity);
-    $build['addtoany'] = [
-      '#addtoany_html' => \Drupal::token()->replace($data['addtoany_html'], ['node' => $entity]),
-      '#link_url' => $data['link_url'],
-      '#link_title' => $data['link_title'],
-      '#button_setting' => $data['button_setting'],
-      '#button_image' => $data['button_image'],
-      '#universal_button_placement' => $data['universal_button_placement'],
-      '#buttons_size' => $data['buttons_size'],
-      '#entity_type' => $entity->getEntityType()->id(),
-      '#bundle' => $entity->bundle(),
-      '#theme' => 'addtoany_standard',
-    ];
+    $config = Drupal::config('addtoany.settings');
+    $isAllowed = $config->get("entities.{$entity->getEntityTypeId()}");
+    if ($isAllowed) {
+      $data = addtoany_create_entity_data($entity);
+      $build['addtoany'] = [
+        '#addtoany_html' => \Drupal::token()->replace($data['addtoany_html'], ['node' => $entity]),
+        '#link_url' => $data['link_url'],
+        '#link_title' => $data['link_title'],
+        '#button_setting' => $data['button_setting'],
+        '#button_image' => $data['button_image'],
+        '#universal_button_placement' => $data['universal_button_placement'],
+        '#buttons_size' => $data['buttons_size'],
+        '#entity_type' => $entity->getEntityType()->id(),
+        '#bundle' => $entity->bundle(),
+        '#theme' => 'addtoany_standard',
+      ];
+    }
   }
 }
 

+ 4 - 0
sites/all/modules/contrib/fields/addtoany/config/install/addtoany.settings.yml

@@ -9,3 +9,7 @@ universal_button: default
 custom_universal_button: ''
 universal_button_placement: before
 no_3p: false
+entities:
+  media: 1
+  node: 1
+  comment: 1

+ 9 - 0
sites/all/modules/contrib/fields/addtoany/css/addtoany.admin.css

@@ -1,3 +1,12 @@
+.addtoany-entity-checkbox ~ .description {
+  display: inline;
+}
+
+.addtoany-entity-checkbox ~ .description a {
+  margin: 0 3px;
+  white-space: nowrap;
+}
+
 .addtoany-round-icon {
   border-radius: 14%;
 }

+ 91 - 2
sites/all/modules/contrib/fields/addtoany/src/Form/AddToAnySettingsForm.php

@@ -5,7 +5,10 @@ namespace Drupal\addtoany\Form;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\ContentEntityType;
+use Drupal\Core\Link;
 
 /**
  * Configure AddToAny settings for this site.
@@ -191,6 +194,69 @@ class AddToAnySettingsForm extends ConfigFormBase {
       ];
     }
 
+    $form['addtoany_entity_settings'] = [
+      '#type'         => 'details',
+      '#title'        => $this->t('Entities'),
+    ];
+
+    $form['addtoany_entity_settings']['addtoany_entity_tip_1'] = [
+      '#type' => 'html_tag',
+      '#tag' => 'p',
+      '#value' => $this
+        ->t('AddToAny is available on the &quot;Manage display&quot; pages of enabled entities, e.g. Structure &gt; Content types &gt; Article &gt; Manage display.'),
+    ];
+
+    $entities = self::getContentEntities();
+
+    // Allow modules to alter the entity types.
+    \Drupal::moduleHandler()->alter('addtoany_entity_types', $entities);
+
+    // Whitelist the entity IDs that let us link to each bundle's Manage Display page.
+    $linkableEntities = [
+      'block_content', 'comment', 'commerce_product', 'commerce_store',
+      'contact_message', 'media', 'node', 'paragraph',
+    ];
+
+    foreach ($entities as $entity) {
+      $entityId = $entity->id();
+      $entityType = $entity->getBundleEntityType();
+      // Get all available bundles for the current entity.
+      $bundles = \Drupal::service('entity.manager')->getBundleInfo($entityId);
+      $links = [];
+
+      foreach($bundles as $machine_name => $bundle) {
+        $label = $bundle['label'];
+
+        // Some labels are TranslatableMarkup objects (such as the File entity).
+        if ($label instanceof TranslatableMarkup) {
+          $label = $label->render();
+        }
+
+        // Link to the bundle's Manage Display page if the entity ID supports the route pattern.
+        if (in_array($entityId, $linkableEntities) && $entityType) {
+          $links[] = Link::createFromRoute(t($label), "entity.entity_view_display.{$entityId}.default", [
+            $entityType => $machine_name,
+          ])->toString();
+        }
+      }
+
+      $description = empty($links) ? '' : '( ' . implode(' | ', $links) . ' )';
+
+      $form['addtoany_entity_settings'][$entityId] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('@entity', ['@entity' => $entity->getLabel()]),
+        '#default_value' => $addtoany_settings->get("entities.{$entityId}"),
+        '#description' => $description,
+        '#attributes' => ['class' => ['addtoany-entity-checkbox']]
+      ];
+    }
+
+    $form['addtoany_entity_settings']['addtoany_entity_tip_2'] = [
+      '#type' => 'html_tag',
+      '#tag' => 'p',
+      '#value' => $this->t('A cache rebuild may be required before changes take effect.'),
+    ];
+
     return parent::buildForm($form, $form_state);
   }
 
@@ -207,10 +273,33 @@ class AddToAnySettingsForm extends ConfigFormBase {
       ->set('custom_universal_button', $values['addtoany_custom_universal_button'])
       ->set('universal_button', $values['addtoany_universal_button'])
       ->set('universal_button_placement', $values['addtoany_universal_button_placement'])
-      ->set('no_3p', $values['addtoany_no_3p'])
-      ->save();
+      ->set('no_3p', $values['addtoany_no_3p']);
+
+    foreach(self::getContentEntities() as $entity) {
+      $entityId = $entity->id();
+      $this->config('addtoany.settings')
+        ->set("entities.{$entityId}", $values[$entityId]);
+    }
+
+    $this->config('addtoany.settings')->save();
 
     parent::submitForm($form, $form_state);
   }
 
+  /**
+   * Get all available content entities in the environment.
+   * @return array
+   */
+  public static function getContentEntities() {
+    $content_entity_types = [];
+    $entity_type_definitions = \Drupal::entityTypeManager()->getDefinitions();
+    /* @var $definition EntityTypeInterface */
+    foreach ($entity_type_definitions as $definition) {
+      if ($definition instanceof ContentEntityType) {
+        $content_entity_types[] = $definition;
+      }
+    }
+
+    return $content_entity_types;
+  }
 }

+ 1 - 1
sites/all/modules/contrib/filters/url_to_video_filter/css/url_to_video_embed.scss

@@ -69,4 +69,4 @@
 		top:0;
 		left:0;
 	}
-}
+}

+ 111 - 64
sites/all/modules/contrib/filters/url_to_video_filter/js/vimeo_embed.js

@@ -1,66 +1,113 @@
+/**
+ * @file
+ * Converts Vimeo placeholders to embedded vimeo videos.
+ */
+
 /*global jQuery, Drupal, drupalSettings*/
-/*jslint white:true, multivar, this, browser:true*/
-
-(function($, Drupal, drupalSettings)
-{
-	"use strict";
-
-	function vimeoVideoEnable(context)
-	{
-		$(context).find(".vimeo-player").once("vimeo-video-enable").each(function()
-		{
-			var vimeoID = $(this).attr("data-vimeo-id");
-
-			$(this).parent().removeClass("no-js");
-
-			if(drupalSettings.urlToVideoFilter.autoload)
-			{
-				$(this).empty().append($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=0&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
-			}
-			else
-			{
-				$.ajax(
-				{
-					url:"//vimeo.com/api/v2/video/" + vimeoID + ".json",
-					context:$(this),
-					success:function(data)
-					{
-						if(data[0] && data[0].thumbnail_large)
-						{
-							$(this).empty().append($("<span/>").append($("<img/>", {class:"player-thumb", src:data[0].thumbnail_large})).append($("<span/>", {class:"play-button"})).click(function(e)
-							{
-								e.stopPropagation();
-
-								Drupal.detachBehaviors($(this).parent().parent());
-
-								$(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=1&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
-							}));
-						}
-						else
-						{
-							$(this).empty().append($("<span/>").append($("<span/>", {class:"play-button"})).click(function(e)
-							{
-								e.stopPropagation();
-
-								Drupal.detachBehaviors($(this).parent().parent());
-
-								$(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=1&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
-							}));
-						}
-					}
-				});
-			}
-		});
-	}
-
-	Drupal.behaviors.urlToVideoFilterVimeo = {
-		attach:function(context)
-		{
-			vimeoVideoEnable(context);
-		},
-		detach:function(context)
-		{
-			$(".vimeo-player", context).unbind("click");
-		}
-	};
+/*jslint white:true, this, browser:true*/
+
+(function ($, Drupal, drupalSettings) {
+  "use strict";
+
+  function vimeoVideoEnable(context) {
+
+    $(context).find(".vimeo-player").once("vimeo-video-enable").each(function () {
+      var IDParts, vimeoID, queryString;
+
+      // Get the VimeoID as well as the query string (if it exists) from the
+      // placeholder.
+      IDParts = $(this).attr("data-vimeo-id").split("?");
+      vimeoID = IDParts[0];
+      // If there is no query string, this will be undefined.
+      queryString = IDParts[1];
+
+      // Remove the no-js class, since there is JS.
+      $(this).parent().removeClass("no-js");
+
+      // If the settings have been set to autoload the Vimeo video:
+      if (drupalSettings.urlToVideoFilter.autoload) {
+        // Embedding happens differently depending on whether or not there is a
+        // query string.
+        if (queryString) {
+          $(this).empty().append($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?" + queryString).attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+        }
+        else {
+          $(this).empty().append($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=0&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+        }
+      }
+      else {
+
+        // The video has not been set to autoload. So an AJAX query is run in
+        // order to get the thumbnail from Vimeo.
+        $.ajax({
+          // The URL is hardcoded to https, as using http causes a redirect
+          // that modern browsers won't allow by default.
+          url:"https://vimeo.com/api/v2/video/" + vimeoID + ".json",
+          context:$(this),
+          success:function (data) {
+            // When a large thumbnail was found for the video:
+            if (data[0] && data[0].thumbnail_large) {
+              // Replace the placeholder contents with the thumbnail, and add
+              // a click handler onto the thumbnail to play the video when the
+              // thumbnail is clicked.
+              $(this).empty().append($("<span/>").append($("<img/>", {class:"player-thumb", src:data[0].thumbnail_large})).append($("<span/>", {class:"play-button"})).click(function (e) {
+
+                // Stop the default click action.
+                e.stopPropagation();
+
+                // The content is about to be replaced with an iframe, so any
+                // handlers on the content that is to be replaced, is passed
+                // through detachBehaviors, to remove the handlers.
+                Drupal.detachBehaviors($(this).parent().parent()[0]);
+
+                // Replace the thumbnail with the iframe.
+                // Replacement differs depending on whether there is a query
+                // string.
+                if (queryString) {
+                  $(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?" + queryString).attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+                }
+                else {
+                  $(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=1&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+                }
+              }));
+            }
+            // No thumbnail was returned Vimeo.
+            else {
+              // Replace the placeholder contents with a play button.
+              $(this).empty().append($("<span/>").append($("<span/>", {class:"play-button"})).click(function (e) {
+
+                // Stop the default click action.
+                e.stopPropagation();
+
+                // The content is about to be replaced with an iframe, so any
+                // handlers on the content that is to be replaced, is passed
+                // through detachBehaviors, to remove the handlers.
+                Drupal.detachBehaviors($(this).parent().parent()[0]);
+
+                // Replace the thumbnail with the iframe.
+                // Replacement differs depending on whether there is a query
+                // string.
+                if (queryString) {
+                  $(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?" + queryString).attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+                }
+                else {
+                  $(this).replaceWith($("<iframe/>").attr("src", "//player.vimeo.com/video/" + vimeoID + "?autopause=1&autoplay=1&badge=1&byline=1&loop=0&portrait=1&autopause=1&fullscreen=1").attr("frameborder", "0").attr("class", "player-iframe vimeo-iframe"));
+                }
+              }));
+            }
+          }
+        });
+      }
+    });
+  }
+
+  Drupal.behaviors.urlToVideoFilterVimeo = {
+    attach:function (context) {
+      vimeoVideoEnable(context);
+    },
+    detach:function (context) {
+      $(".vimeo-player", context).unbind("click");
+    }
+  };
+
 }(jQuery, Drupal, drupalSettings));

+ 99 - 54
sites/all/modules/contrib/filters/url_to_video_filter/js/youtube_embed.js

@@ -1,55 +1,100 @@
-/*global jQuery, Drupal, drupalSettings*/
-/*jslint white:true, multivar, this, browser:true*/
-
-(function($, Drupal, drupalSettings)
-{
-	"use strict";
-
-	function youtubeVideoEnable(context)
-	{
-		$(context).find(".youtube-player").once("youtube-video-enable").each(function()
-		{
-			var youtubeID, youtubePreview;
-
-			youtubeID = $(this).attr("data-youtube-id");
-
-			$(this).parent().removeClass("no-js");
-
-			if(drupalSettings.urlToVideoFilter.autoload)
-			{
-				$(this).empty().append($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?autoplay=0&autohide=2&border=0&wmode=opaque&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
-			}
-			else
-			{
-				if(drupalSettings.urlToVideoFilter.youtubeWebp)
-				{
-					youtubePreview = "//i.ytimg.com/vi_webp/" + youtubeID + "/sddefault.webp";
-				}
-				else
-				{
-					youtubePreview = "//i.ytimg.com/vi/" + youtubeID + "/hqdefault.jpg";
-				}
-
-				$(this).empty().append($("<span/>").append($("<div/>", {class:"player-thumb",style:"background-image:url(" + youtubePreview + ")"})).append($("<span/>", {class:"play-button"})).click(function(e)
-				{
-					e.stopPropagation();
-
-					Drupal.detachBehaviors($(this).parent().parent());
-
-					$(this).replaceWith($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?autoplay=1&autohide=2&border=0&wmode=opaque&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
-				}));
-			}
-		});
-	}
-
-	Drupal.behaviors.urlToVideoFilterYoutube = {
-		attach:function(context)
-		{
-			youtubeVideoEnable(context);
-		},
-		detach:function(context)
-		{
-			$(".youtube-player", context).unbind("click");
-		}
-	};	
+/**
+ * @file
+ * Replaces placeholders for youtube videos with thumbnails and videos.
+ */
+
+ /*global jQuery, Drupal, drupalSettings*/
+ /*jslint white:true, this, browser:true*/
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Handles youtube thumbnail embedding as well as iframe insertion.
+   */
+  function youtubeVideoEnable(context) {
+
+    $(context).find(".youtube-player").once("youtube-video-enable").each(function () {
+
+      // Declare the variables to be used in the script.
+      var IDParts, youtubeID, youtubePreview, queryString;
+
+      // The YouTube ID is stored in the data-youtube-id attribute of the
+      // placeholder in the HTML. This value may or may not have a query string
+      // appended to it, so the value is split by the query string separator (?)
+      // if it exists in the ID.
+      IDParts = $(this).data("youtube-id").split("?");
+      // Fetch the unique YouTube ID.
+      youtubeID = IDParts[0];
+      // Fetch the query string. This will be undefined if there was no query
+      // string in the HTML placeholder.
+      queryString = IDParts[1];
+
+      // As JavaScript is enabled, the no-js class is removed.
+      $(this).parent().removeClass("no-js");
+
+      // Check if the videos are set to autoload (rather than using thumbnail)
+      // placeholders).
+      if (drupalSettings.urlToVideoFilter.autoload) {
+        // Need to handle embedding differently if there is a query string.
+        if (queryString) {
+          // Embed the iframe into the page with the query string.
+          $(this).empty().append($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?" + queryString + "&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
+        }
+        else {
+          // Embed the iframe into the page.
+          $(this).empty().append($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?autoplay=0&autohide=2&border=0&wmode=opaque&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
+        }
+      }
+      // Videos are not set to autoload, and will instead show a thumbnail
+      // placeholder.
+      else {
+
+        // Check if Webp images have been enabled.
+        if (drupalSettings.urlToVideoFilter.youtubeWebp) {
+          // Fetch the Webp URL for the thumbnail.
+          youtubePreview = "//i.ytimg.com/vi_webp/" + youtubeID + "/sddefault.webp";
+        }
+        else {
+          // Fetch the URL for the thumbnail.
+          youtubePreview = "//i.ytimg.com/vi/" + youtubeID + "/hqdefault.jpg";
+        }
+
+        // Empty the placeholder, and replace it with the YouTube thumbnail.
+        // Also add a click handler to the image so the video will play when
+        // the thumbnail is clicked.
+        $(this).empty().append($("<span/>").append($("<div/>", {class:"player-thumb",style:"background-image:url(" + youtubePreview + ")"})).append($("<span/>", {class:"play-button"})).click(function (e) {
+
+          // Stop the default click action.
+          e.stopPropagation();
+
+          // The content is about to be replaced, so .detachBehaviors() is first
+          // run on the content about to be deleted, so as to remove any handler
+          // preventing memory leaks.
+          Drupal.detachBehaviors($(this).parent().parent()[0]);
+
+          // Need to handle embedding differently if there is a query string.
+          if (queryString) {
+            // Embed the iframe into the page with the query string.
+            $(this).replaceWith($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?" + queryString + "&autoplay=1&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
+          }
+          else {
+            // Embed the iframe into the page.
+            $(this).replaceWith($("<iframe/>").attr("src", "//www.youtube.com/embed/" + youtubeID + "?autoplay=1&autohide=2&border=0&wmode=opaque&enablejsapi=1").attr("frameborder", "0").attr("class", "player-iframe youtube-iframe"));
+          }
+        }));
+      }
+    });
+  }
+
+  Drupal.behaviors.urlToVideoFilterYoutube = {
+    attach:function (context) {
+      youtubeVideoEnable(context);
+    },
+    detach:function (context) {
+      $(".youtube-player", context).unbind("click");
+    }
+  };
+
 }(jQuery, Drupal, drupalSettings));

+ 129 - 131
sites/all/modules/contrib/filters/url_to_video_filter/src/Plugin/Filter/FilterUrlToVideo.php

@@ -25,135 +25,133 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   }
  * )
  */
-class FilterUrlToVideo extends FilterBase implements ContainerFactoryPluginInterface
-{
-	/**
-	 * The Url to Video Filter Service
-	 *
-	 * @var \Drupal\url_to_video_filter\Service\UrlToVideoFilterServiceInterface
-	 */
-	protected $urlToVideoFilterService;
-
-	/**
-	 * Constructs a UrlToVideoFilter object
-	 *
-	 * @param array $configuration
-	 * @param string $plugin_id
-	 * @param mixed $plugin_definition
-	 * @param \Drupal\url_to_video_filter\Service\UrlToVideoFilterServiceInterface $urlToVideoFilterService
-	 *   The Url to Video Filter Service
-	 */
-	public function __construct(array $configuration, $plugin_id, $plugin_definition, UrlToVideoFilterServiceInterface $urlToVideoFilterService)
-	{
-		parent::__construct($configuration, $plugin_id, $plugin_definition);
-
-		$this->urlToVideoFilterService = $urlToVideoFilterService;
-	}
-
-	/**
-	 * {@inheritdoc}
-	 */
-	static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition)
-	{
-		return new static(
-			$configuration,
-			$plugin_id,
-			$plugin_definition,
-			$container->get('url_to_video_filter.service')
-		);
-	}
-
-	public function process($text, $langcode)
-	{
-		$youtube_found = FALSE;
-		$vimeo_found = FALSE;
-
-		// Process YouTube URLs
-		if($this->settings['youtube'])
-		{
-			$filter = $this->urlToVideoFilterService->convertYouTubeUrls($text);
-			$text = $filter['text'];
-			$youtube_found = $filter['url_found'];
-		}
-
-		// Process Vimeo Urls
-		if($this->settings['vimeo'])
-		{
-			$filter = $this->urlToVideoFilterService->convertVimeoUrls($text);
-			$text = $filter['text'];
-			$vimeo_found = $filter['url_found'];
-		}
-
-		$libraries = [];
-		$result = new FilterProcessResult($text);
-		if($youtube_found)
-		{
-			$libraries[] = 'url_to_video_filter/youtube_embed';
-		}
-
-		if($vimeo_found)
-		{
-			$libraries[] = 'url_to_video_filter/vimeo_embed';
-		}
-
-		$js_settings['urlToVideoFilter'] = [];
-
-		if($this->settings['youtube'] && $this->settings['youtube_webp_preview'])
-		{
-			$js_settings['urlToVideoFilter']['youtubeWebp'] = TRUE;
-		}
-
-		if($this->settings['autoload'])
-		{
-			$js_settings['urlToVideoFilter']['autoload'] = TRUE;
-		}
-
-		$result->setAttachments([
-			'drupalSettings' => $js_settings,
-			'library' => $libraries,
-		]);
-
-		return $result;
-	}
-
-	/**
-	 * {@inheritdoc}
-	 */
-	public function settingsForm(array $form, FormStateInterface $form_state)
-	{
-		// Enable filtering for YouTube URLs
-		$form['youtube'] = [
-			'#type' => 'checkbox',
-			'#title' => $this->t('Filter for YouTube URLs'),
-			'#default_value' => $this->settings['youtube'],
-		];
-
-		$form['youtube_webp_preview'] = [
-			'#type' => 'checkbox',
-			'#title' => $this->t('Use webp image for YouTube preview'),
-			'#description'=> $this->t('Warning - not compatible with some browsers'),
-			'#default_value' => $this->settings['youtube_webp_preview'],
-			'#states' => [
-				'visible' => [
-					'#edit-filters-filter-url-to-video-settings-youtube' => ['checked' => TRUE],
-					'#edit-filters-filter-url-to-video-settings-autoload' => ['checked' => FALSE],
-				],
-			],
-		];
-
-		// Enable filtering for Vimeo URLs
-		$form['vimeo'] = [
-			'#type' => 'checkbox',
-			'#title' => $this->t('Filter for Vimeo URLs'),
-			'#default_value' => $this->settings['vimeo'],
-		];
-
-		$form['autoload'] = [
-			'#type' => 'checkbox',
-			'#title' => $this->t('Autoload players when page has loaded.'),
-			'#default_value' => $this->settings['autoload'],
-		];
-
-		return $form;
-	}
+class FilterUrlToVideo extends FilterBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The Url to Video Filter Service.
+   *
+   * @var \Drupal\url_to_video_filter\Service\UrlToVideoFilterServiceInterface
+   */
+  protected $urlToVideoFilterService;
+
+  /**
+   * Constructs a UrlToVideoFilter object.
+   *
+   * @param array $configuration
+   *   The plugin configuration.
+   * @param string $plugin_id
+   *   The plugin ID.
+   * @param mixed $plugin_definition
+   *   The plugin definition.
+   * @param \Drupal\url_to_video_filter\Service\UrlToVideoFilterServiceInterface $urlToVideoFilterService
+   *   The Url to Video Filter Service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, UrlToVideoFilterServiceInterface $urlToVideoFilterService) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->urlToVideoFilterService = $urlToVideoFilterService;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('url_to_video_filter.service')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process($text, $langcode) {
+    $youtube_found = FALSE;
+    $vimeo_found = FALSE;
+
+    // Process YouTube URLs.
+    if ($this->settings['youtube']) {
+      $filter = $this->urlToVideoFilterService->convertYouTubeUrls($text);
+      $text = $filter['text'];
+      $youtube_found = $filter['url_found'];
+    }
+
+    // Process Vimeo Urls.
+    if ($this->settings['vimeo']) {
+      $filter = $this->urlToVideoFilterService->convertVimeoUrls($text);
+      $text = $filter['text'];
+      $vimeo_found = $filter['url_found'];
+    }
+
+    $libraries = [];
+    $result = new FilterProcessResult($text);
+    if ($youtube_found) {
+      $libraries[] = 'url_to_video_filter/youtube_embed';
+    }
+
+    if ($vimeo_found) {
+      $libraries[] = 'url_to_video_filter/vimeo_embed';
+    }
+
+    $js_settings['urlToVideoFilter'] = [];
+
+    if ($this->settings['youtube'] && $this->settings['youtube_webp_preview']) {
+      $js_settings['urlToVideoFilter']['youtubeWebp'] = TRUE;
+    }
+
+    if ($this->settings['autoload']) {
+      $js_settings['urlToVideoFilter']['autoload'] = TRUE;
+    }
+
+    $result->setAttachments([
+      'drupalSettings' => $js_settings,
+      'library' => $libraries,
+    ]);
+
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+
+    // Enable filtering for YouTube URLs.
+    $form['youtube'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Filter for YouTube URLs'),
+      '#default_value' => $this->settings['youtube'],
+    ];
+
+    $form['youtube_webp_preview'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Use webp image for YouTube preview'),
+      '#description' => $this->t('Warning - not compatible with some browsers'),
+      '#default_value' => $this->settings['youtube_webp_preview'],
+      '#states' => [
+        'visible' => [
+          '#edit-filters-filter-url-to-video-settings-youtube' => ['checked' => TRUE],
+          '#edit-filters-filter-url-to-video-settings-autoload' => ['checked' => FALSE],
+        ],
+      ],
+    ];
+
+    // Enable filtering for Vimeo URLs.
+    $form['vimeo'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Filter for Vimeo URLs'),
+      '#default_value' => $this->settings['vimeo'],
+    ];
+
+    $form['autoload'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Autoload players when page has loaded.'),
+      '#default_value' => $this->settings['autoload'],
+    ];
+
+    return $form;
+  }
+
 }

+ 156 - 144
sites/all/modules/contrib/filters/url_to_video_filter/src/Service/UrlToVideoFilterService.php

@@ -2,148 +2,160 @@
 
 namespace Drupal\url_to_video_filter\Service;
 
-use Drupal\url_to_video_filter\Service\UrlToVideoFilterServiceInterface;
-
-class UrlToVideoFilterService implements UrlToVideoFilterServiceInterface
-{
-	/**
-	 * {@inheritdoc}
-	 */
-	public function convertYouTubeUrls($text)
-	{
-		$return = [
-			'text' => $text,
-			'url_found' => FALSE,
-		];
-
-		$urls = $this->parseYouTubeUrls($text);
-		if(count($urls))
-		{
-			$return['url_found'] = TRUE;
-			foreach($urls as $url)
-			{
-				$embed_code = $this->convertYouTubeUrlToEmbedCode($url);
-				$return['text'] = str_replace($url, $embed_code, $return['text']);
-			}
-		}
-
-		return $return;
-	}
-
-	/**
-	 * {@inheritdoc}
-	 */
-	public function convertVimeoUrls($text)
-	{
-		$return = [
-			'text' => $text,
-			'url_found' => FALSE,
-		];
-
-		$urls = $this->parseVimeoUrls($text);
-		if(count($urls))
-		{
-			$return['url_found'] = TRUE;
-			foreach($urls as $url)
-			{
-				$embed_code = $this->convertVimeoUrlToEmbedCode($url);
-				$return['text'] = str_replace($url, $embed_code, $return['text']);
-			}
-		}
-
-		return $return;
-	}
-
-	/**
-	 * Parses text for YouTube URLs
-	 *
-	 * @param $text
-	 *   The text to parse for YouTube URLs.
-	 *
-	 * @return array
-	 *   An array containing any YouTube URLs found in the text
-	 */
-	protected function parseYouTubeUrls($text)
-	{
-		$urls = array();
-
-		$youtube_regex = '/(^|\b)http(s)?:\/\/(www\.)?youtube\.com\/watch.+?(?=($|\s|\r|\r\n|\n|<))/m';
-		preg_match_all($youtube_regex, $text, $matches);
-		$urls = array_merge($urls, $matches[0]);
-
-		$youtube_regex = '/(^|\b)http(s)?:\/\/(www\.)?youtu\.be\/.+?(?=($|\s|\r|\r\n|\n|<))/m';
-		preg_match_all($youtube_regex, $text, $matches);
-		$urls = array_merge($urls, $matches[0]);
-
-		return $urls;
-	}
-
-	/**
-	 * Convert YouTube URL to YouTube embed text
-	 *
-	 * @param string $url
-	 *   The YouTube URL to convert
-	 *
-	 * @return string
-	 *   HTML containing the embed code for the YouTube video
-	 *   for the given URL.
-	 */
-	protected function convertYouTubeUrlToEmbedCode($url)
-	{
-		$embed_code = '';
-
-		if(strpos($url, 'youtube.com'))
-		{
-			$video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?youtube\.com\/watch\?v=/', '', $url);
-		}
-		elseif(strpos($url, 'youtu.be'))
-		{
-			$video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?youtu.be\//', '', $url);
-		}
-
-		$embed_code .= '<span class="url-to-video-container youtube-container no-js">';
-		$embed_code .= '<span class="youtube-player url-to-video-player loader" data-youtube-id="'. $video_key . '"></span>';
-		$embed_code .= '</span>';
-
-		return $embed_code;
-	}
-
-	/**
-	 * Parses text for Vimeo URLs
-	 *
-	 * @param $text
-	 *   The text to parse for Vimeo URLs.
-	 *
-	 * @return array
-	 *   An array containing any Vimeo URLs found in the text
-	 */
-	protected function parseVimeoUrls($text)
-	{
-		$vimeo_regex = '/(^|\b)http(s)?:\/\/(www\.)?vimeo\.com.+?(?=($|\s|\r|\r\n|\n|<))/m';
-		preg_match_all($vimeo_regex, $text, $matches);
-
-		return $matches[0];
-	}
-
-	/**
-	 * Convert Vimeo URL to YouTube embed text
-	 *
-	 * @param string $url
-	 *   The Vimeo URL to convert
-	 *
-	 * @return string
-	 *   HTML containing the embed code for the Vimeo video
-	 *   for the given URL.
-	 */
-	protected function convertVimeoUrlToEmbedCode($url)
-	{
-		$embed_code = '';
-
-		$video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?vimeo\.com\//', '', $url);
-		$embed_code .= '<span class="url-to-video-container vimeo-container no-js">';
-		$embed_code .= '<span class="vimeo-player url-to-video-player loader" data-vimeo-id="'. $video_key . '"></span>';
-		$embed_code .= '</span>';
-
-		return $embed_code;
-	}
+/**
+ * Service class for the URL To Video Filter module.
+ *
+ * Performs various actions used by the module.
+ */
+class UrlToVideoFilterService implements UrlToVideoFilterServiceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convertYouTubeUrls($text) {
+    $return = [
+      'text' => $text,
+      'url_found' => FALSE,
+    ];
+
+    $urls = $this->parseYouTubeUrls($text);
+    if (count($urls)) {
+      $return['url_found'] = TRUE;
+      foreach ($urls as $url) {
+        $embed_code = $this->convertYouTubeUrlToEmbedCode($url);
+        $return['text'] = str_replace($url, $embed_code, $return['text']);
+      }
+    }
+
+    return $return;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convertVimeoUrls($text) {
+    $return = [
+      'text' => $text,
+      'url_found' => FALSE,
+    ];
+
+    $urls = $this->parseVimeoUrls($text);
+    if (count($urls)) {
+      $return['url_found'] = TRUE;
+      foreach ($urls as $url) {
+        $embed_code = $this->convertVimeoUrlToEmbedCode($url);
+        $return['text'] = str_replace($url, $embed_code, $return['text']);
+      }
+    }
+
+    return $return;
+  }
+
+  /**
+   * Parses text for YouTube URLs.
+   *
+   * @param string $text
+   *   The text to parse for YouTube URLs.
+   *
+   * @return array
+   *   An array containing any YouTube URLs found in the text
+   */
+  protected function parseYouTubeUrls($text) {
+    $urls = [];
+
+    $youtube_regex = '/(^|\b)http(s)?:\/\/(www\.)?youtube\.com\/watch.+?(?=($|\s|\r|\r\n|\n|<))/m';
+    preg_match_all($youtube_regex, $text, $matches);
+    $urls = array_merge($urls, $matches[0]);
+
+    $youtube_regex = '/(^|\b)http(s)?:\/\/(www\.)?youtu\.be\/.+?(?=($|\s|\r|\r\n|\n|<))/m';
+    preg_match_all($youtube_regex, $text, $matches);
+    $urls = array_merge($urls, $matches[0]);
+
+    $youtube_regex = '/(^|\b)http(s)?:\/\/(www\.)?youtube\.com\/embed.+?(?=($|\s|\r|\r\n|\n|<))/m';
+    preg_match_all($youtube_regex, $text, $matches);
+    $urls = array_merge($urls, $matches[0]);
+
+    return $urls;
+  }
+
+  /**
+   * Converts YouTube URL to HTML placeholders.
+   *
+   * Note that ths function only embeds placeholders into the HTML. The actual
+   * content, whether that be a clickable thumbnail, or the embedded video
+   * itself, is injected into the HTML with JavaScript, replacing the
+   * placeholders this function embeds.
+   *
+   * Converts for URLs of the following patterns:
+   *   - youtube.com/watch?v=##########
+   *   - youtube.com/embed/###########
+   *   - youtu.be/###########
+   *
+   * @param string $url
+   *   The YouTube URL to convert.
+   *
+   * @return string
+   *   HTML containing the placeholder for the YouTube video for the given URL.
+   */
+  protected function convertYouTubeUrlToEmbedCode($url) {
+    $embed_code = '';
+
+    if (strpos($url, 'youtube.com/watch')) {
+      $video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?youtube\.com\/watch\?v=/', '', $url);
+    }
+    elseif (strpos($url, 'youtube.com/embed')) {
+      $video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?youtube\.com\/embed\//', '', $url);
+    }
+    elseif (strpos($url, 'youtu.be')) {
+      $video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?youtu.be\//', '', $url);
+    }
+
+    $embed_code .= '<span class="url-to-video-container youtube-container no-js">';
+    $embed_code .= '<span class="youtube-player url-to-video-player loader" data-youtube-id="' . $video_key . '"></span>';
+    $embed_code .= '</span>';
+
+    return $embed_code;
+  }
+
+  /**
+   * Parses text for Vimeo URLs.
+   *
+   * @param string $text
+   *   The text to parse for Vimeo URLs.
+   *
+   * @return array
+   *   An array containing any Vimeo URLs found in the text
+   */
+  protected function parseVimeoUrls($text) {
+    $vimeo_regex = '/(^|\b)http(s)?:\/\/(www\.)?vimeo\.com.+?(?=($|\s|\r|\r\n|\n|<))/m';
+    preg_match_all($vimeo_regex, $text, $matches);
+
+    return $matches[0];
+  }
+
+  /**
+   * Converts Vimeo URL to HTML placeholders.
+   *
+   * Note that ths function only embeds placeholders into the HTML. The actual
+   * content, whether that be a clickable thumbnail, or the embedded video
+   * itself, is injected into the HTML with JavaScript, replacing the
+   * placeholders this function embeds.
+   *
+   * @param string $url
+   *   The Vimeo URL to convert.
+   *
+   * @return string
+   *   HTML containing the placeholder for the Vimeo video for the given URL.
+   */
+  protected function convertVimeoUrlToEmbedCode($url) {
+    $embed_code = '';
+
+    $video_key = preg_replace('/(http)?(s)?(:\/\/)?(www\.)?vimeo\.com\//', '', $url);
+    $embed_code .= '<span class="url-to-video-container vimeo-container no-js">';
+    $embed_code .= '<span class="vimeo-player url-to-video-player loader" data-vimeo-id="' . $video_key . '"></span>';
+    $embed_code .= '</span>';
+
+    return $embed_code;
+  }
+
 }

+ 32 - 28
sites/all/modules/contrib/filters/url_to_video_filter/src/Service/UrlToVideoFilterServiceInterface.php

@@ -2,33 +2,37 @@
 
 namespace Drupal\url_to_video_filter\Service;
 
-interface UrlToVideoFilterServiceInterface
-{
-	/**
-	 * Converts URLs to embedded YouTube videos
-	 *
-	 * @param string $text
-	 *   The text to be parsed for YouTube URLs
-	 *
-	 * @return array
-	 *   An array containing the following keys:
-	 *   - text: The text with the URLs replaced by the YouTube embed code
-	 *   - url_found: A boolean indicating whether any URLs were found in
-	 *     the given text.
-	 */
-	public function convertYouTubeUrls($text);
+/**
+ * Interface for the URL To Video service class.
+ */
+interface UrlToVideoFilterServiceInterface {
+
+  /**
+   * Converts URLs to embedded YouTube videos.
+   *
+   * @param string $text
+   *   The text to be parsed for YouTube URLs.
+   *
+   * @return array
+   *   An array containing the following keys:
+   *   - text: The text with the URLs replaced by the YouTube embed code
+   *   - url_found: A boolean indicating whether any URLs were found in
+   *     the given text.
+   */
+  public function convertYouTubeUrls($text);
+
+  /**
+   * Converts URLs to embedded Vimeo videos.
+   *
+   * @param string $text
+   *   The text to be parsed for Vimeo URLs.
+   *
+   * @return array
+   *   An array containing the following keys:
+   *   - text: The text with the URLs replaced by the YouTube embed code
+   *   - url_found: A boolean indicating whether any URLs were found in
+   *     the given text.
+   */
+  public function convertVimeoUrls($text);
 
-	/**
-	 * Converts URLs to embedded Vimeo videos
-	 *
-	 * @param string $text
-	 *   The text to be parsed for Vimeo URLs
-	 *
-	 * @return array
-	 *   An array containing the following keys:
-	 *   - text: The text with the URLs replaced by the YouTube embed code
-	 *   - url_found: A boolean indicating whether any URLs were found in
-	 *     the given text.
-	 */
-	public function convertVimeoUrls($text);
 }

+ 3 - 5
sites/all/modules/contrib/filters/url_to_video_filter/url_to_video_filter.info.yml

@@ -3,10 +3,8 @@ description: Text filter to convert URLs to embedded videos
 type: module
 # core: 8.x
 
-configure: 'admin/config/media/url_to_video_filter'
-
-# Information added by Drupal.org packaging script on 2017-04-10
-version: '8.x-1.1'
+# Information added by Drupal.org packaging script on 2018-07-27
+version: '8.x-1.3'
 core: '8.x'
 project: 'url_to_video_filter'
-datestamp: 1491842044
+datestamp: 1532695990

+ 3 - 0
sites/all/modules/contrib/filters/url_to_video_filter/url_to_video_filter.libraries.yml

@@ -7,6 +7,7 @@ youtube_embed:
   js:
     js/youtube_embed.js: {}
   dependencies:
+    - core/drupal
     - core/jquery
     - core/drupalSettings
     - core/jquery.once
@@ -16,6 +17,8 @@ vimeo_embed:
   js:
     js/vimeo_embed.js: {}
   dependencies:
+    - core/drupal
     - core/jquery
     - core/drupalSettings
+    - core/jquery.once
     - url_to_video_filter/player_embed

+ 22 - 13
sites/all/modules/contrib/views/translation_views/README.md

@@ -1,19 +1,21 @@
-Translation View
+Translation Views
 =============
 
-## Description:
+INTRODUCTION
+------------
 Provides fields and filter for views to display information about the current row, the **source** translation, in another **target** language. You can also add translation operation links to decide if you want to add/edit the content in the target language.
 
-Configurations for a demo view is impored on installation, named `Content translation jobs` which can be found at path `/translate/content`. If you want to get view for other translatable entities, you should build it yourself using our fields/filters.
+Configurations for a demo view is imported on installation, named `Content translation jobs` which can be found at path `/translate/content`. If you want to get view for other translatable entities, you should build it yourself using our fields/filters.
 
-**Notice**: This module provides view's fields/filters only for translatble entity types, this means that until you have enabled content translation support for an entity you'll not get provided any extra fields/filters. The content translations can be enabled at path `admin/config/regional/content-language`
+**Notice**: This module provides view's fields/filters only for translatable entity types, this means that until you have enabled content translation support for an entity you'll not get provided any extra fields/filters. The content translations can be enabled at path `admin/config/regional/content-language`
 
-## Features
+FEATURES
+------------
 
 **Target translation**
 
 - Target language: `field`,`filter`. Should in most cases be an exposed filter.
-  Also there is an extra filter option: *Remove rows where source language is equal to target language*
+  Also, there is an extra filter option: *Remove rows where source language is equal to target language*
 
 These fields/filters will display information about the current content in the selected target language:
 - Translation outdated: `field`,`filter`
@@ -26,17 +28,23 @@ These fields/filters will display information about the current content in the s
 **Source translation**
 - Translation counter: `field`,`filter`
 
-## Requirements:
+REQUIREMENTS
+------------
 - Drupal 8
-- PHP 5.6+
+- PHP 7+
 
-## Installation:
+INSTALLATION
+------------
 Install module as usually.
 
+CONFIGURATION
+-------------
+No configuration is needed.
+
 ### Demo view: Content translation jobs:
-Upon installation a new view is added named `Content translation jobs` that demonstrate how the fields/filters can be used to create lists of translation jobs. Available at path `/translate/content`.
+Upon installation, a new view is added named `Content translation jobs` that demonstrate how the fields/filters can be used to create lists of translation jobs. Available at path `/translate/content`.
 
-If this didn't happened on installation than go to the`admin/config/development/configuration/single/import` and manually import config from the file located in the module folder at: `config/optional/views.view.content_translations.yml`.
+If this didn't happened on installation then go to the`admin/config/development/configuration/single/import` and manually import config from the file located in the module folder at: `config/optional/views.view.content_translations.yml`.
 
 **Fields**
 The view display:
@@ -57,9 +65,10 @@ Exposed filters:
 Hidden filters
 - Source content outdated must be false: Make sure you do not translation from outdated nodes.
 - Target language equals default language must be false: As there is no use to check status of original language as a target language.
-- The target language must be untranslated or outdated: Make sure translated and updated content disapear from the list.
+- The target language must be untranslated or outdated: Make sure translated and updated content disappear from the list.
 - The target language must be untranslated or the same as the source language of translation: This makes sure that once there is a translation in target language it will only be displayed if the source language is the correct source language. If target language is not translated you can use different language as source translation.
 
-## Credits
+MAINTAINERS
+-----------
 Developed by [vlad.dancer](https://drupal.org/u/vladdancer)  
 Designed by [matsbla](https://drupal.org/u/matsbla)

+ 7 - 8
sites/all/modules/contrib/views/translation_views/config/install/views.view.content_translations.yml → sites/all/modules/contrib/views/translation_views/config/optional/views.view.content_translations.yml

@@ -297,7 +297,7 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          native_language: 0
+          native_language: false
           entity_type: node
           plugin_id: translation_views_target_language
         translation_outdated:
@@ -600,7 +600,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: {  }
+          value: ''
           group: 1
           exposed: true
           expose:
@@ -629,7 +629,7 @@ display:
             default_group: All
             default_group_multiple: {  }
             group_items: {  }
-          remove: 1
+          remove: true
           entity_type: node
           plugin_id: translation_views_target_language
         translation_outdated:
@@ -679,12 +679,12 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: '2'
+          value: 2
           group: 1
           exposed: true
           expose:
             operator_id: ''
-            label: Translation status
+            label: 'Translation status'
             description: ''
             use_operator: false
             operator: translation_status_op
@@ -708,7 +708,6 @@ display:
             default_group: All
             default_group_multiple: {  }
             group_items: {  }
-          translation_target_language: false
           entity_type: node
           plugin_id: translation_views_status
         translation_default:
@@ -755,7 +754,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: '0'
+          value: 0
           group: 2
           exposed: false
           expose:
@@ -828,7 +827,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: '0'
+          value: 0
           group: 3
           exposed: false
           expose:

+ 44 - 0
sites/all/modules/contrib/views/translation_views/config/schema/translation_views.views.schema.yml

@@ -4,3 +4,47 @@ views.filter.translation_views_translation_count:
 
 views.filter_value.translation_views_translation_count:
   type: views.filter_value.numeric
+  label: 'Filter rows by amount of translations (without default language)'
+
+views.filter.translation_views_source_equals_row:
+  type: views.filter.boolean
+  value: 'A boolean indicating whether the translation source of target language is same as the row language'
+
+views.filter.translation_views_status:
+  type: views_filter
+  label: 'A value indicating whether the node is translated into target language'
+  mapping:
+    value:
+      type: integer
+      label: 'Value'
+
+views.filter.translation_views_target_language:
+  type: views_filter
+  label: 'Target language'
+  mapping:
+    value:
+      type: string
+      label: 'Value'
+    remove:
+      type: boolean
+      label: 'Remove rows where source language is equal to target language'
+
+views.field.translation_views_translation_count:
+  type: views.field.numeric
+  label: 'Amount of translations (without default language)'
+
+views.field.translation_views_operations:
+  type: views_field
+  label: 'Provides links to perform translation operations'
+
+views.field.translation_views_source_equals_row:
+  type: views.field.boolean
+  label: 'A boolean indicating whether the translation source of target language is same as the row language'
+
+views.field.translation_views_status:
+  type: views.field.boolean
+  label: 'A boolean indicating whether the node is translated into target language'
+
+views.field.translation_views_target_language:
+  type: views.field.language
+  label: 'The target language'

+ 7 - 0
sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationCountField.php

@@ -19,6 +19,13 @@ use Drupal\views\Plugin\ViewsHandlerManager;
 class TranslationCountField extends NumericField implements ContainerFactoryPluginInterface {
   use TranslationCountTrait;
 
+  /**
+   * Translation table alias.
+   *
+   * @var string
+   */
+  public $tableAlias = 'translations';
+
   /**
    * {@inheritdoc}
    */

+ 1 - 1
sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationSourceLangcodeEqualsRowLangcodeField.php

@@ -37,7 +37,7 @@ class TranslationSourceLangcodeEqualsRowLangcodeField extends Boolean {
    */
   public function query() {
     $table_alias = $this->ensureMyTable();
-    $base_table  = $this->view->storage->get('base_table');
+    $base_table = $this->view->storage->get('base_table');
 
     $this->query->addField(
       NULL,

+ 1 - 1
sites/all/modules/contrib/views/translation_views/src/Plugin/views/field/TranslationStatus.php

@@ -92,4 +92,4 @@ class TranslationStatus extends Boolean implements ContainerFactoryPluginInterfa
     );
   }
 
-}
+}

+ 7 - 0
sites/all/modules/contrib/views/translation_views/src/Plugin/views/filter/TranslationCountFilter.php

@@ -21,6 +21,13 @@ use Drupal\views\Plugin\ViewsHandlerManager;
 class TranslationCountFilter extends NumericFilter implements ContainerFactoryPluginInterface {
   use TranslationCountTrait;
 
+  /**
+   * Translation table alias.
+   *
+   * @var string
+   */
+  public $tableAlias = 'translations';
+
   /**
    * {@inheritdoc}
    */

+ 1 - 1
sites/all/modules/contrib/views/translation_views/src/Plugin/views/filter/TranslationSourceLangcodeEqualsRowLangcodeFilter.php

@@ -18,7 +18,7 @@ class TranslationSourceLangcodeEqualsRowLangcodeFilter extends BooleanOperator {
    */
   public function query() {
     $table_alias = $this->ensureMyTable();
-    $base_table  = $this->view->storage->get('base_table');
+    $base_table = $this->view->storage->get('base_table');
 
     $this->query->addWhereExpression(
       $this->options['group'],

+ 9 - 7
sites/all/modules/contrib/views/translation_views/src/Plugin/views/join/TranslationLanguageJoin.php

@@ -21,12 +21,15 @@ class TranslationLanguageJoin extends JoinPluginBase implements ContainerFactory
    */
   protected $database;
 
+  protected $eid;
+
   /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->database = $database;
+    $this->eid = $this->configuration['entity_pk'];
   }
 
   /**
@@ -45,21 +48,20 @@ class TranslationLanguageJoin extends JoinPluginBase implements ContainerFactory
    * {@inheritdoc}
    */
   public function buildJoin($select_query, $table, $view_query) {
-    $query = $this->database->select($this->table, 'nfd');
-    $query->fields('nfd', ['nid']);
+    $query = $this->database->select($this->table, 'efd');
+    $query->fields('efd', [$this->eid]);
 
     if (!empty($this->configuration['langcodes_as_count'])) {
-      $query->addExpression("COUNT(nfd.langcode)", 'count_langs');
+      $query->addExpression("COUNT(efd.langcode)", 'count_langs');
     }
     else {
-      $query->addExpression("GROUP_CONCAT(nfd.langcode separator ',')", 'langs');
+      $query->addExpression("GROUP_CONCAT(efd.langcode separator ',')", 'langs');
     }
 
     if (!empty($this->configuration['exclude_default_langcode'])) {
-      $query->where('nfd.default_langcode != 1');
+      $query->where('efd.default_langcode != 1');
     }
-
-    $query->groupBy('nfd.nid');
+    $query->groupBy('efd.' . $this->eid);
     $this->configuration['table formula'] = $query;
 
     parent::buildJoin($select_query, $table, $view_query);

+ 1 - 7
sites/all/modules/contrib/views/translation_views/src/TranslationCountTrait.php

@@ -12,13 +12,6 @@ use Drupal\views\Plugin\views\query\Sql as ViewsSqlQuery;
  */
 trait TranslationCountTrait {
 
-  /**
-   * Translation table alias.
-   *
-   * @var string
-   */
-  protected $tableAlias = 'translations';
-
   /**
    * Ensure that translations table is joined.
    *
@@ -43,6 +36,7 @@ trait TranslationCountTrait {
         'left_table' => $query_base_table,
         'exclude_default_langcode' => TRUE,
         'langcodes_as_count' => TRUE,
+        'entity_pk'  => $keys['id'],
       ];
 
       $tableAlias = $query->ensureTable($query_base_table, $this->relationship);

+ 996 - 0
sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/test_views/views.view.comment_translation.yml

@@ -0,0 +1,996 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - comment
+    - node
+    - translation_views
+    - user
+id: comment_translation
+label: 'Comment translation'
+module: views
+description: ''
+tag: ''
+base_table: comment_field_data
+base_field: cid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access comments'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: ‹‹
+            next: ››
+      style:
+        type: table
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          override: true
+          sticky: false
+          caption: ''
+          summary: ''
+          description: ''
+          columns:
+            subject: subject
+            langcode: langcode
+            translation_target_language: translation_target_language
+            translation_default: translation_default
+            translation_changed: translation_changed
+            translation_count: translation_count
+            translation_operations: translation_operations
+            translation_outdated: translation_outdated
+            translation_source: translation_source
+            translation_status: translation_status
+          info:
+            subject:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            langcode:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_target_language:
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_default:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_changed:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_count:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_operations:
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_outdated:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_source:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_status:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+          default: '-1'
+          empty_table: false
+      row:
+        type: fields
+      relationships:
+        node:
+          id: node
+          table: comment_field_data
+          field: node
+          entity_type: comment_field_data
+          required: true
+          plugin_id: standard
+          relationship: none
+          group_type: group
+          admin_label: Content
+      fields:
+        subject:
+          id: subject
+          table: comment_field_data
+          field: subject
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Title
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: false
+            ellipsis: false
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          settings:
+            link_to_entity: false
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          entity_type: comment
+          entity_field: subject
+          plugin_id: field
+        langcode:
+          id: langcode
+          table: comment_field_data
+          field: langcode
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: language
+          settings:
+            link_to_entity: false
+            native_language: false
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          entity_type: comment
+          entity_field: langcode
+          plugin_id: field
+        translation_target_language:
+          id: translation_target_language
+          table: comment_translation
+          field: translation_target_language
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Target language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          native_language: false
+          entity_type: comment
+          plugin_id: translation_views_target_language
+        translation_default:
+          id: translation_default
+          table: comment_translation_target
+          field: translation_default
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Target language equals default language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: default_langcode
+          plugin_id: boolean
+        translation_changed:
+          id: translation_changed
+          table: comment_translation_target
+          field: translation_changed
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation changed time'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          date_format: fallback
+          custom_date_format: ''
+          timezone: ''
+          entity_field: changed
+          plugin_id: date
+        translation_count:
+          id: translation_count
+          table: comment_translation
+          field: translation_count
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation counter'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ','
+          format_plural: false
+          format_plural_string: !!binary MQNAY291bnQ=
+          prefix: ''
+          suffix: ''
+          entity_type: comment
+          plugin_id: translation_views_translation_count
+        translation_outdated:
+          id: translation_outdated
+          table: comment_translation_target
+          field: translation_outdated
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation outdated'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: content_translation_outdated
+          plugin_id: boolean
+        translation_source:
+          id: translation_source
+          table: comment_translation_target
+          field: translation_source
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation source equals row language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: content_translation_source
+          plugin_id: translation_views_source_equals_row
+        translation_status:
+          id: translation_status
+          table: comment_translation
+          field: translation_status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation status'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: status
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_type: comment
+          plugin_id: translation_views_status
+        translation_operations:
+          id: translation_operations
+          table: comment_translation
+          field: translation_operations
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation operations'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          destination: false
+          entity_type: comment
+          plugin_id: translation_views_operations
+      filters:
+        status:
+          value: '1'
+          table: comment_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: comment
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        status_node:
+          value: '1'
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          relationship: node
+          entity_type: node
+          entity_field: status
+          id: status_node
+          expose:
+            operator: ''
+          group: 1
+        langcode:
+          id: langcode
+          table: comment_field_data
+          field: langcode
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: langcode_op
+            label: 'Translation language'
+            description: ''
+            use_operator: false
+            operator: langcode_op
+            identifier: langcode
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: comment
+          entity_field: langcode
+          plugin_id: language
+        translation_target_language:
+          id: translation_target_language
+          table: comment_translation
+          field: translation_target_language
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: '***LANGUAGE_site_default***'
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Target language'
+            description: ''
+            use_operator: false
+            operator: translation_target_language_op
+            identifier: translation_target_language
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          remove: false
+          entity_type: comment
+          plugin_id: translation_views_target_language
+        translation_outdated:
+          id: translation_outdated
+          table: comment_translation_target
+          field: translation_outdated
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: All
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Translation outdated'
+            description: ''
+            use_operator: false
+            operator: translation_outdated_op
+            identifier: translation_outdated
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_field: content_translation_outdated
+          plugin_id: boolean
+        translation_status:
+          id: translation_status
+          table: comment_translation
+          field: translation_status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: 2
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Translation status'
+            description: ''
+            use_operator: false
+            operator: translation_status_op
+            identifier: translation_status
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: comment
+          plugin_id: translation_views_status
+        translation_count:
+          id: translation_count
+          table: comment_translation
+          field: translation_count
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value:
+            min: ''
+            max: ''
+            value: ''
+          group: 1
+          exposed: true
+          expose:
+            operator_id: translation_count_op
+            label: 'Translation counter'
+            description: ''
+            use_operator: false
+            operator: translation_count_op
+            identifier: translation_count
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+            placeholder: ''
+            min_placeholder: ''
+            max_placeholder: ''
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: comment
+          plugin_id: translation_views_translation_count
+      sorts: {  }
+      title: 'Comment translation'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      arguments: {  }
+      display_extenders: {  }
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - user.permissions
+      tags: {  }
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: 1
+    display_options:
+      display_extenders: {  }
+      path: comment-translation
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - user.permissions
+      tags: {  }

+ 924 - 0
sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/test_views/views.view.translation_views_all_filters_and_fields.yml

@@ -0,0 +1,924 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - node
+    - translation_views
+    - user
+id: translation_views_all_filters_and_fields
+label: 'Translation views all filters and fields'
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: ‹‹
+            next: ››
+      style:
+        type: table
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          override: true
+          sticky: false
+          caption: ''
+          summary: ''
+          description: ''
+          columns:
+            title: title
+            langcode: langcode
+            translation_default: translation_default
+            translation_changed: translation_changed
+            translation_count: translation_count
+            translation_outdated: translation_outdated
+            translation_source: translation_source
+            translation_status: translation_status
+            translation_operations: translation_operations
+          info:
+            title:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            langcode:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_default:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_changed:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_count:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_outdated:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_source:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_status:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            translation_operations:
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+          default: '-1'
+          empty_table: false
+      row:
+        type: fields
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          alter:
+            alter_text: false
+            make_link: false
+            absolute: false
+            trim: false
+            word_boundary: false
+            ellipsis: false
+            strip_tags: false
+            html: false
+          hide_empty: false
+          empty_zero: false
+          settings:
+            link_to_entity: true
+          plugin_id: field
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Title
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+        langcode:
+          id: langcode
+          table: node_field_data
+          field: langcode
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: language
+          settings:
+            link_to_entity: false
+            native_language: false
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          entity_type: node
+          entity_field: langcode
+          plugin_id: field
+        translation_target_language:
+          id: translation_target_language
+          table: node_translation
+          field: translation_target_language
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Target language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          native_language: false
+          entity_type: node
+          plugin_id: translation_views_target_language
+        translation_default:
+          id: translation_default
+          table: node_translation_target
+          field: translation_default
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Target language equals default language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: default_langcode
+          plugin_id: boolean
+        translation_changed:
+          id: translation_changed
+          table: node_translation_target
+          field: translation_changed
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation changed time'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          date_format: fallback
+          custom_date_format: ''
+          timezone: ''
+          entity_field: changed
+          plugin_id: date
+        translation_count:
+          id: translation_count
+          table: node_translation
+          field: translation_count
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation counter'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ','
+          format_plural: false
+          format_plural_string: !!binary MQNAY291bnQ=
+          prefix: ''
+          suffix: ''
+          entity_type: node
+          plugin_id: translation_views_translation_count
+        translation_outdated:
+          id: translation_outdated
+          table: node_translation_target
+          field: translation_outdated
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation outdated'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: content_translation_outdated
+          plugin_id: boolean
+        translation_source:
+          id: translation_source
+          table: node_translation_target
+          field: translation_source
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation source equals row language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_field: content_translation_source
+          plugin_id: translation_views_source_equals_row
+        translation_status:
+          id: translation_status
+          table: node_translation
+          field: translation_status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation status'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          type: status
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+          entity_type: node
+          plugin_id: translation_views_status
+        translation_operations:
+          id: translation_operations
+          table: node_translation
+          field: translation_operations
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Translation operations'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          destination: false
+          entity_type: node
+          plugin_id: translation_views_operations
+      filters:
+        status:
+          value: '1'
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        content_translation_source:
+          id: content_translation_source
+          table: node_field_data
+          field: content_translation_source
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: content_translation_source_op
+            label: 'Translation source'
+            description: ''
+            use_operator: false
+            operator: content_translation_source_op
+            identifier: content_translation_source
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: node
+          entity_field: content_translation_source
+          plugin_id: language
+        translation_target_language:
+          id: translation_target_language
+          table: node_translation
+          field: translation_target_language
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: ''
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Target language'
+            description: ''
+            use_operator: false
+            operator: translation_target_language_op
+            identifier: translation_target_language
+            required: false
+            remember: true
+            multiple: false
+            remember_roles:
+              anonymous: anonymous
+              authenticated: authenticated
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          remove: true
+          entity_type: node
+          plugin_id: translation_views_target_language
+        translation_default:
+          id: translation_default
+          table: node_translation_target
+          field: translation_default
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: All
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Target language equals default language'
+            description: ''
+            use_operator: false
+            operator: translation_default_op
+            identifier: translation_default
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: 'Target language equals default language'
+            description: null
+            identifier: translation_default
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items:
+              1: {  }
+              2: {  }
+              3: {  }
+          entity_field: default_langcode
+          plugin_id: boolean
+        translation_status:
+          id: translation_status
+          table: node_translation
+          field: translation_status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: 2
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Translation status'
+            description: ''
+            use_operator: false
+            operator: translation_status_op
+            identifier: translation_status
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: node
+          plugin_id: translation_views_status
+      sorts:
+        created:
+          id: created
+          table: node_field_data
+          field: created
+          order: DESC
+          entity_type: node
+          entity_field: created
+          plugin_id: date
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          granularity: second
+      title: 'Translation views all filters and fields'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: 1
+    display_options:
+      display_extenders: {  }
+      path: translation-views-all-filters-and-fields
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }

+ 17 - 0
sites/all/modules/contrib/views/translation_views/tests/modules/translation_views_test_views/translation_views_test_views.info.yml

@@ -0,0 +1,17 @@
+name: 'Translation views test view'
+type: module
+description: 'Provides full view for translation_views tests.'
+package: Testing
+# core: 8.x
+dependencies:
+  - drupal:node
+  - drupal:views
+  - drupal:translation_views
+  - drupal:content_translation
+  - drupal:language
+
+# Information added by Drupal.org packaging script on 2018-05-23
+version: '8.x-1.0-alpha2'
+core: '8.x'
+project: 'translation_views'
+datestamp: 1527090186

+ 491 - 0
sites/all/modules/contrib/views/translation_views/tests/src/Functional/CommentFullViewFiltersFieldsTest.php

@@ -0,0 +1,491 @@
+<?php
+
+namespace Drupal\Tests\translation_views\Functional;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\Tests\views\Functional\ViewTestBase;
+use Drupal\views\Tests\ViewTestData;
+
+/**
+ * Tests for fields, filters and sorting for comment entity.
+ *
+ * @group translation_views
+ */
+class CommentFullViewFiltersFieldsTest extends ViewTestBase {
+
+  protected $profile = 'standard';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['translation_views_test_views'];
+
+  public $loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget mi mi.';
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = ['comment_translation'];
+
+  private $viewPath = 'comment-translation';
+
+  /**
+   * Checks that text is in specific row.
+   *
+   * @param int $row_number
+   *   Table row order number.
+   * @param string $css_class
+   *   Part of the css class of required field.
+   * @param string $text
+   *   Text that should be found in the element.
+   *
+   * @throws \Behat\Mink\Exception\ElementTextException
+   */
+  private function assertTextInRow($row_number, $css_class, $text) {
+    $this->assertSession()
+      ->elementTextContains('css', "table > tbody > tr:nth-child({$row_number}) td.views-field-{$css_class}", $text);
+  }
+
+  /**
+   * Cyclic check that text is in specific row.
+   *
+   * @param string $css_class
+   *   Part of the css class of required field.
+   * @param array $texts
+   *   Array of texts that should be found in the element
+   *   and rows' order number.
+   *
+   * @throws \Behat\Mink\Exception\ElementTextException
+   */
+  private function assertTextInRowOrder($css_class, array $texts) {
+    foreach ($texts as $id => $text) {
+      $this->assertTextInRow($id, $css_class, $text);
+    }
+  }
+
+  /**
+   * Adds languages to Drupal.
+   *
+   * @param array $langcodes
+   *   Langcodes that should be added to Drupal.
+   *
+   * @throws \Drupal\Core\Entity\EntityStorageException
+   */
+  private function addLanguages(array $langcodes) {
+    foreach ($langcodes as $langcode) {
+      ConfigurableLanguage::createFromLangcode($langcode)->save();
+    }
+  }
+
+  /**
+   * Change language settings for entity types.
+   *
+   * @param string $category
+   *   Entity category (e.g. Content).
+   * @param string $subcategory
+   *   Entity subcategory (e.g. Article).
+   */
+  private function enableTranslation($category, $subcategory) {
+    $this->drupalPostForm('admin/config/regional/content-language', [
+      "entity_types[$category]"                                                   => 1,
+      "settings[$category][$subcategory][translatable]"                           => 1,
+      "settings[$category][$subcategory][settings][language][language_alterable]" => 1,
+    ], t('Save configuration'));
+    \Drupal::entityTypeManager()->clearCachedDefinitions();
+  }
+
+  /**
+   * Set column sorting and order.
+   *
+   * @param string $orderColumn
+   *   Machine name of the column to be sorted.
+   * @param string $sort
+   *   Sorting order (asc or desc).
+   * @param array $default_params
+   *   Langcode and Translation target language.
+   *
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  private function assertOrder($orderColumn, $sort, array $default_params = []) {
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'langcode'                    => $default_params[0],
+        'translation_target_language' => $default_params[1],
+        'translation_outdated'        => 'All',
+        'translation_status'          => 2,
+        'order'                       => $orderColumn,
+        'sort'                        => $sort,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp($import_test_views = TRUE) {
+    parent::setUp($import_test_views);
+
+    $user = $this->drupalCreateUser([
+      'administer content types',
+      'edit own comments',
+      'administer comments',
+      'administer comment types',
+      'administer comment fields',
+      'administer comment display',
+      'post comments',
+      'access comments',
+      'skip comment approval',
+      'access content',
+      'administer site configuration',
+      'administer nodes',
+      'administer views',
+      'create article content',
+      'edit any article content',
+      'administer content translation',
+      'translate any entity',
+      'create content translations',
+      'administer languages',
+    ]);
+
+    $this->drupalLogin($user);
+
+    ViewTestData::createTestViews(get_class($this), ['translation_views_test_views']);
+
+    // Add two languages.
+    $this->addLanguages(['de', 'fr']);
+
+    // Enable translation for Comment entity type.
+    $this->enableTranslation('comment', 'comment');
+
+    // Add 3 nodes.
+    $this->drupalPostForm('node/add/article', [
+      'title[0][value]'         => 'node 1',
+      'created[0][value][date]' => '2018-04-01',
+    ], t('Save'));
+    $this->drupalPostForm('node/add/article', [
+      'title[0][value]'         => 'node 2',
+      'created[0][value][date]' => '2018-04-02',
+    ], t('Save'));
+    $this->drupalPostForm('node/add/article', [
+      'title[0][value]'         => 'node 3',
+      'created[0][value][date]' => '2018-04-03',
+    ], t('Save'));
+
+    // Add comments and translations.
+    // Comment 1.
+    $edit = [
+      'langcode[0][value]'     => 'en',
+      'comment_body[0][value]' => $this->loremIpsum,
+      'subject[0][value]'      => 'node 1 en comment',
+    ];
+    $this->drupalPostForm('node/1', $edit, 'Save');
+    // Comment 2.
+    $edit = [
+      'langcode[0][value]'     => 'de',
+      'comment_body[0][value]' => $this->loremIpsum,
+      'subject[0][value]'      => 'node 1 de comment',
+    ];
+    $this->drupalPostForm('node/1', $edit, 'Save');
+    // Comment 3.
+    $edit = [
+      'langcode[0][value]'     => 'de',
+      'comment_body[0][value]' => $this->loremIpsum,
+      'subject[0][value]'      => 'node 2 de comment',
+    ];
+    $this->drupalPostForm('node/2', $edit, 'Save');
+    // Comment 4.
+    $edit = [
+      'langcode[0][value]'     => 'fr',
+      'comment_body[0][value]' => $this->loremIpsum,
+      'subject[0][value]'      => 'node 2 fr comment',
+    ];
+    $this->drupalPostForm('node/2', $edit, 'Save');
+    // Comment 5.
+    $edit = [
+      'langcode[0][value]'     => 'fr',
+      'comment_body[0][value]' => $this->loremIpsum,
+      'subject[0][value]'      => 'node 3 fr comment',
+    ];
+    $this->drupalPostForm('node/3', $edit, 'Save');
+    // Comment 6.
+    $edit = [
+      'content_translation[retranslate]' => 1,
+      'comment_body[0][value]'           => $this->loremIpsum,
+      'subject[0][value]'                => 'node 3 en from fr comment',
+    ];
+    $this->drupalPostForm('comment/5/translations/add/fr/en', $edit, 'Save');
+  }
+
+  /**
+   * Tests that the fields show all required information.
+   */
+  public function testFields() {
+    $this->drupalGet($this->viewPath);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertTextInRow(1, 'subject', 'node 1 en comment');
+    $this->assertTextInRow(1, 'langcode', 'English');
+    $this->assertTextInRow(1, 'translation-target-language', 'English');
+    $this->assertTextInRow(1, 'translation-default', 'Yes');
+    $this->assertTextInRow(1, 'translation-changed', ':');
+    $this->assertTextInRow(1, 'translation-count', '0');
+    $this->assertTextInRow(1, 'translation-outdated', 'No');
+    $this->assertTextInRow(1, 'translation-source', 'No');
+    $this->assertTextInRow(1, 'translation-status', 'Translated');
+    $this->assertTextInRow(1, 'translation-operations', 'Edit');
+  }
+
+  /**
+   * Tests columns' sorting.
+   */
+  public function testSorting() {
+    // Title.
+    $this->assertOrder('subject', 'asc', ['de', 'fr']);
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 1 de comment',
+      2 => 'node 2 de comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 en comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 en from fr comment');
+
+    $this->assertOrder('subject', 'desc', ['de', 'fr']);
+    $this->assertTextInRowOrder('subject', [
+      2 => 'node 1 de comment',
+      1 => 'node 2 de comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 en comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 en from fr comment');
+
+    // Translation language.
+    $this->assertOrder('langcode', 'asc', ['All', 'de']);
+    $this->assertTextInRowOrder('langcode', [
+      1 => 'German',
+      2 => 'German',
+      3 => 'English',
+      4 => 'English',
+      5 => 'French',
+      6 => 'French',
+    ]);
+
+    $this->assertOrder('langcode', 'desc', ['All', 'de']);
+    $this->assertTextInRowOrder('langcode', [
+      6 => 'German',
+      5 => 'German',
+      4 => 'English',
+      3 => 'English',
+      2 => 'French',
+      1 => 'French',
+    ]);
+
+    // Target language equals default language.
+    $this->assertOrder('translation_default', 'asc', ['All', 'fr']);
+    $this->assertTextInRowOrder('translation-default', [
+      1 => 'No',
+      2 => 'No',
+      3 => 'No',
+      4 => 'Yes',
+      5 => 'Yes',
+      6 => 'Yes',
+    ]);
+
+    $this->assertOrder('translation_default', 'desc', ['All', 'fr']);
+    $this->assertTextInRowOrder('translation-default', [
+      6 => 'No',
+      5 => 'No',
+      4 => 'No',
+      3 => 'Yes',
+      2 => 'Yes',
+      1 => 'Yes',
+    ]);
+
+    // Translation changed time.
+    $this->assertOrder('translation_changed', 'asc', ['All', 'fr']);
+    $this->assertTextInRowOrder('translation-changed', [
+      4 => ':',
+      5 => ':',
+      6 => ':',
+    ]);
+
+    $this->assertOrder('translation_changed', 'desc', ['All', 'fr']);
+    $this->assertTextInRowOrder('translation-changed', [
+      1 => ':',
+      2 => ':',
+      3 => ':',
+    ]);
+
+    // Translation counter.
+    $this->assertOrder('translation_count', 'asc', ['en', 'fr']);
+    $this->assertTextInRowOrder('translation-count', [
+      1 => '0',
+      2 => '1',
+    ]);
+
+    $this->assertOrder('translation_count', 'desc', ['en', 'fr']);
+    $this->assertTextInRowOrder('translation-count', [
+      2 => '0',
+      1 => '1',
+    ]);
+
+    // Translation outdated.
+    $this->assertOrder('translation-outdated', 'asc', ['en', 'fr']);
+    $this->assertTextInRowOrder('translation-outdated', [
+      1 => 'No',
+      2 => 'Yes',
+    ]);
+
+    $this->assertOrder('translation-outdated', 'desc', ['en', 'fr']);
+    $this->assertTextInRowOrder('translation-outdated', [
+      1 => 'No',
+      2 => 'Yes',
+    ]);
+
+    // Translation source equals row language.
+    $this->assertOrder('translation_source', 'asc', [
+      'All',
+      '***LANGUAGE_site_default***',
+    ]);
+    $this->assertTextInRowOrder('translation-source', [
+      1 => 'No',
+      2 => 'No',
+      3 => 'No',
+      4 => 'No',
+      5 => 'No',
+      6 => 'Yes',
+    ]);
+
+    $this->assertOrder('translation_source', 'desc', [
+      'All',
+      '***LANGUAGE_site_default***',
+    ]);
+    $this->assertTextInRowOrder('translation-source', [
+      6 => 'No',
+      5 => 'No',
+      4 => 'No',
+      3 => 'No',
+      2 => 'No',
+      1 => 'Yes',
+    ]);
+
+    // Translation status.
+    $this->assertOrder('translation_status', 'asc', [
+      'All',
+      '***LANGUAGE_site_default***',
+    ]);
+    $this->assertTextInRowOrder('translation-status', [
+      1 => 'Not translated',
+      2 => 'Not translated',
+      3 => 'Not translated',
+      4 => 'Translated',
+      5 => 'Translated',
+      6 => 'Translated',
+    ]);
+
+    $this->assertOrder('translation_status', 'desc', [
+      'All',
+      '***LANGUAGE_site_default***',
+    ]);
+    $this->assertTextInRowOrder('translation-status', [
+      6 => 'Not translated',
+      5 => 'Not translated',
+      4 => 'Not translated',
+      3 => 'Translated',
+      2 => 'Translated',
+      1 => 'Translated',
+    ]);
+
+  }
+
+  /**
+   * Tests that filters are working correctly.
+   */
+  public function testFilters() {
+
+    // Translation language.
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'langcode' => 'en',
+      ],
+    ]);
+
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 1 en comment',
+      2 => 'node 3 en from fr comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+
+    // Translation language & Target language.
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'langcode'                    => 'en',
+        'translation_target_language' => 'fr',
+      ],
+    ]);
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 1 en comment',
+      2 => 'node 3 en from fr comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+
+    // Translation language & Target language & Translation outdated.
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'langcode'                    => 'en',
+        'translation_target_language' => 'fr',
+        'translation_outdated'        => '1',
+      ],
+    ]);
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 3 en from fr comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 en comment');
+    $this->assertSession()->pageTextNotContains('node 1 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+
+    // Translation status #1.
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'translation_status' => '0',
+      ],
+    ]);
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 1 de comment',
+      2 => 'node 2 de comment',
+      3 => 'node 2 fr comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 en comment');
+    $this->assertSession()->pageTextNotContains('node 3 fr comment');
+    $this->assertSession()->pageTextNotContains('node 3 en from fr comment');
+
+    // Translation status #2.
+    $this->drupalGet($this->viewPath, [
+      'query' => [
+        'translation_status' => '1',
+      ],
+    ]);
+    $this->assertTextInRowOrder('subject', [
+      1 => 'node 1 en comment',
+      2 => 'node 3 en from fr comment',
+      3 => 'node 3 fr comment',
+    ]);
+    $this->assertSession()->pageTextNotContains('node 1 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 de comment');
+    $this->assertSession()->pageTextNotContains('node 2 fr comment');
+  }
+
+}

+ 471 - 0
sites/all/modules/contrib/views/translation_views/tests/src/Functional/ContentFullViewFiltersFieldsTest.php

@@ -0,0 +1,471 @@
+<?php
+
+namespace Drupal\Tests\translation_views\Functional;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\Tests\views\Functional\ViewTestBase;
+use Drupal\views\Tests\ViewTestData;
+
+/**
+ * Tests for fields, filters and sorting for content entity.
+ *
+ * @group translation_views
+ */
+class ContentFullViewFiltersFieldsTest extends ViewTestBase {
+
+  protected $profile = 'standard';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['translation_views_test_views'];
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = ['translation_views_all_filters_and_fields'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp($import_test_views = TRUE) {
+    parent::setUp($import_test_views);
+
+    $user = $this->drupalCreateUser([
+      'administer site configuration',
+      'administer nodes',
+      'administer views',
+      'create article content',
+      'access content',
+      'edit any article content',
+      'administer content translation',
+      'translate any entity',
+      'create content translations',
+      'administer languages',
+      'administer content types',
+    ]);
+    $this->drupalLogin($user);
+
+    ViewTestData::createTestViews(get_class($this), ['translation_views_test_views']);
+
+    $langcodes = ['de', 'fr'];
+    foreach ($langcodes as $langcode) {
+      ConfigurableLanguage::createFromLangcode($langcode)->save();
+    }
+
+    // Enable translation for article.
+    $edit = [
+      'entity_types[node]'                                              => 1,
+      'settings[node][article][translatable]'                           => 1,
+      'settings[node][article][settings][language][language_alterable]' => 1,
+    ];
+    $this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
+    \Drupal::entityTypeManager()->clearCachedDefinitions();
+
+    // Create a node in en (node1).
+    $edit = [
+      'title[0][value]'    => '001_en_title_node1',
+      'langcode[0][value]' => 'en',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+    // Create a translation in fr (node1).
+    $edit = [
+      'title[0][value]' => '002_fr_title_node1',
+    ];
+    $this->drupalPostForm('node/1/translations/add/en/fr', $edit, t('Save (this translation)'));
+    // Create a translation in de (node1).
+    $edit = [
+      'title[0][value]' => '003_de_title_node1',
+    ];
+    $this->drupalPostForm('node/1/translations/add/en/de', $edit, t('Save (this translation)'));
+
+    // Create a node in de (node2).
+    $edit = [
+      'title[0][value]'    => '004_de_title_node2',
+      'langcode[0][value]' => 'de',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+    // Create a translation in fr (node2).
+    $edit = [
+      'title[0][value]' => '005_fr_title_node2',
+    ];
+    $this->drupalPostForm('node/2/translations/add/de/fr', $edit, t('Save (this translation)'));
+
+  }
+
+  /**
+   * Tests the filtering.
+   */
+  public function testFilters() {
+    $this->drupalGet('translation-views-all-filters-and-fields');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->pageTextContains('Translation views all filters and fields');
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextContains('002_fr_title_node1');
+    $this->assertSession()->pageTextContains('003_de_title_node1');
+    $this->assertSession()->pageTextContains('004_de_title_node2');
+    $this->assertSession()->pageTextContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => '***LANGUAGE_site_default***',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextContains('002_fr_title_node1');
+    $this->assertSession()->pageTextContains('003_de_title_node1');
+    $this->assertSession()->pageTextNotContains('004_de_title_node2');
+    $this->assertSession()->pageTextNotContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'de',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextNotContains('002_fr_title_node1');
+    $this->assertSession()->pageTextNotContains('003_de_title_node1');
+    $this->assertSession()->pageTextNotContains('004_de_title_node2');
+    $this->assertSession()->pageTextContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'fr',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextNotContains('002_fr_title_node1');
+    $this->assertSession()->pageTextNotContains('003_de_title_node1');
+    $this->assertSession()->pageTextNotContains('004_de_title_node2');
+    $this->assertSession()->pageTextNotContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => 'de',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('001_en_title_node1');
+    $this->assertSession()->pageTextContains('002_fr_title_node1');
+    $this->assertSession()->pageTextNotContains('003_de_title_node1');
+    $this->assertSession()->pageTextNotContains('004_de_title_node2');
+    $this->assertSession()->pageTextContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => 'fr',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('001_en_title_node1');
+    $this->assertSession()->pageTextNotContains('002_fr_title_node1');
+    $this->assertSession()->pageTextContains('003_de_title_node1');
+    $this->assertSession()->pageTextContains('004_de_title_node2');
+    $this->assertSession()->pageTextNotContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 1,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextContains('002_fr_title_node1');
+    $this->assertSession()->pageTextContains('003_de_title_node1');
+    $this->assertSession()->pageTextNotContains('004_de_title_node2');
+    $this->assertSession()->pageTextNotContains('005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 0,
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('001_en_title_node1');
+    $this->assertSession()->pageTextNotContains('002_fr_title_node1');
+    $this->assertSession()->pageTextNotContains('003_de_title_node1');
+    $this->assertSession()->pageTextContains('004_de_title_node2');
+    $this->assertSession()->pageTextContains('005_fr_title_node2');
+  }
+
+  /**
+   * Test the sorting.
+   */
+  public function testSorting() {
+    // Title.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'title',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', '002_fr_title_node1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', '003_de_title_node1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', '004_de_title_node2');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', '005_fr_title_node2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'title',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', '002_fr_title_node1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', '003_de_title_node1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', '004_de_title_node2');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', '005_fr_title_node2');
+
+    // Translation language.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'langcode',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'German');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'German');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'French');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'French');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'langcode',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'German');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'German');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'French');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'French');
+
+    // Target language equals default language.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_default',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'No');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'No');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'Yes');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'Yes');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_default',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'No');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'No');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'Yes');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'Yes');
+
+    // Translation changed time.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_changed',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextNotContains('css', 'table > tbody > tr:nth-child(1)', ':');
+    $this->assertSession()
+      ->elementTextNotContains('css', 'table > tbody > tr:nth-child(2)', ':');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', ':');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', ':');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_changed',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextNotContains('css', 'table > tbody > tr:nth-child(4)', ':');
+    $this->assertSession()
+      ->elementTextNotContains('css', 'table > tbody > tr:nth-child(3)', ':');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', ':');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', ':');
+
+    // Translation counter.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_count',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', '1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', '1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', '2');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', '2');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_count',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', '1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', '1');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', '2');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', '2');
+
+    // Translation counter.
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_status',
+        'sort'                        => 'asc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'Not translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'Not translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'Translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'Translated');
+
+    $this->drupalGet('translation-views-all-filters-and-fields', [
+      'query' => [
+        'content_translation_source'  => 'All',
+        'translation_target_language' => '***LANGUAGE_site_default***',
+        'translation_default'         => 'All',
+        'translation_status'          => 2,
+        'order'                       => 'translation_status',
+        'sort'                        => 'desc',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(4)', 'Not translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'Not translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'Translated');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'Translated');
+  }
+
+}

+ 223 - 0
sites/all/modules/contrib/views/translation_views/tests/src/Functional/ContentTranslationJobsViewTest.php

@@ -0,0 +1,223 @@
+<?php
+
+namespace Drupal\Tests\translation_views\Functional;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests for fields, filters and sorting (Content translation jobs view).
+ *
+ * @group translation_views
+ */
+class ContentTranslationJobsViewTest extends BrowserTestBase {
+
+  protected $profile = 'standard';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'node',
+    'translation_views',
+    'language',
+    'content_translation',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $langcodes = ['de', 'fr'];
+    foreach ($langcodes as $langcode) {
+      ConfigurableLanguage::createFromLangcode($langcode)->save();
+    }
+
+    $user = $this->drupalCreateUser([
+      'administer site configuration',
+      'administer nodes',
+      'create article content',
+      'access content',
+      'edit any article content',
+      'administer content translation',
+      'translate any entity',
+      'create content translations',
+      'administer languages',
+      'administer content types',
+    ]);
+    $this->drupalLogin($user);
+
+    // Enable translation for article.
+    $edit = [
+      'entity_types[node]'                                              => 1,
+      'settings[node][article][translatable]'                           => 1,
+      'settings[node][article][settings][language][language_alterable]' => 1,
+    ];
+    $this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
+    \Drupal::entityTypeManager()->clearCachedDefinitions();
+
+    // Create node1 in german.
+    $edit = [
+      'title[0][value]'    => 'node1 de',
+      'langcode[0][value]' => 'de',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+
+    // Create node1 translation in french.
+    $edit = [
+      'title[0][value]'    => 'node1 fr',
+    ];
+    $this->drupalPostForm('node/1/translations/add/de/fr', $edit, t('Save (this translation)'));
+
+    // Create node2 in english.
+    $edit = [
+      'title[0][value]'    => 'node2 en',
+      'langcode[0][value]' => 'en',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+
+    // Create node3 in french.
+    $edit = [
+      'title[0][value]'    => 'node3 fr',
+      'langcode[0][value]' => 'fr',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+  }
+
+  /**
+   * Tests that filters works as expected.
+   */
+  public function testNodesFiltering() {
+    $this->drupalGet('translate/content', ['query' => ['translation_target_language' => '***LANGUAGE_site_default***']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('node1 de');
+    $this->assertSession()->pageTextContains('node1 fr');
+    $this->assertSession()->pageTextNotContains('node2 en');
+    $this->assertSession()->pageTextContains('node3 fr');
+
+    $this->drupalGet('translate/content', ['query' => ['translation_target_language' => 'de']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextContains('node2 en');
+    $this->assertSession()->pageTextContains('node3 fr');
+
+    $this->drupalGet('translate/content', ['query' => ['translation_target_language' => 'fr']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextContains('node2 en');
+    $this->assertSession()->pageTextNotContains('node3 fr');
+
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'translation_target_language' => 'de',
+        'langcode'                    => 'en',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextContains('node2 en');
+    $this->assertSession()->pageTextNotContains('node3 fr');
+
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'translation_target_language' => 'fr',
+        'langcode'                    => 'en',
+      ],
+    ]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextContains('node2 en');
+    $this->assertSession()->pageTextNotContains('node3 fr');
+
+    $this->drupalGet('translate/content', ['query' => ['langcode' => 'fr']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextContains('node1 fr');
+    $this->assertSession()->pageTextNotContains('node2 en');
+    $this->assertSession()->pageTextContains('node3 fr');
+
+    $this->drupalGet('translate/content', ['query' => ['langcode' => 'de']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextNotContains('node2 en');
+    $this->assertSession()->pageTextNotContains('node3 fr');
+
+    $this->drupalGet('translate/content', ['query' => ['langcode' => 'en']]);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('node1 de');
+    $this->assertSession()->pageTextNotContains('node1 fr');
+    $this->assertSession()->pageTextNotContains('node2 en');
+    $this->assertSession()->pageTextNotContains('node3 fr');
+
+  }
+
+  /**
+   * Tests that columns' sorting works as expected.
+   */
+  public function testNodesSorting() {
+    // Check title column asc sorting.
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'order' => 'title',
+        'sort'  => 'asc',
+      ],
+    ]);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'node1 de');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'node1 fr');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'node3 fr');
+
+    // Check title column desc sorting.
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'order' => 'title',
+        'sort'  => 'desc',
+      ],
+    ]);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'node1 de');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'node1 fr');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'node3 fr');
+
+    // Check langcode column asc sorting.
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'order' => 'langcode',
+        'sort'  => 'asc',
+      ],
+    ]);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'node1 de');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'node1 fr');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'node3 fr');
+
+    // Check langcode column desc sorting.
+    $this->drupalGet('translate/content', [
+      'query' => [
+        'order' => 'langcode',
+        'sort'  => 'desc',
+      ],
+    ]);
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(1)', 'node1 fr');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(2)', 'node3 fr');
+    $this->assertSession()
+      ->elementTextContains('css', 'table > tbody > tr:nth-child(3)', 'node1 de');
+  }
+
+}

+ 3 - 3
sites/all/modules/contrib/views/translation_views/translation_views.info.yml

@@ -7,8 +7,8 @@ dependencies:
   - drupal:content_translation
   - drupal:views
 
-# Information added by Drupal.org packaging script on 2018-02-13
-version: '8.x-1.0-alpha1'
+# Information added by Drupal.org packaging script on 2018-05-23
+version: '8.x-1.0-alpha2'
 core: '8.x'
 project: 'translation_views'
-datestamp: 1518516495
+datestamp: 1527090186

+ 25 - 0
sites/all/modules/contrib/views/translation_views/translation_views.module

@@ -12,6 +12,7 @@ use Drupal\Core\Database\Query\AlterableInterface;
 use Drupal\Core\Entity\ContentEntityFormInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
  * Implements hook_module_implements_alter().
@@ -112,3 +113,27 @@ function translation_views_query_views_alter(AlterableInterface $query) {
     }
   }
 }
+
+/**
+ * Implements hook_help().
+ *
+ * @inheritdoc
+ */
+function translation_views_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    case 'help.page.translation_views':
+      $text = file_get_contents(dirname(__FILE__) . '/README.md');
+      if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
+        return '<pre>' . $text . '</pre>';
+      }
+      else {
+        // Use the Markdown filter to render the README.
+        $filter_manager = \Drupal::service('plugin.manager.filter');
+        $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
+        $config = ['settings' => $settings];
+        $filter = $filter_manager->createInstance('markdown', $config);
+        return $filter->process($text, 'en');
+      }
+  }
+  return NULL;
+}

+ 16 - 15
sites/all/modules/contrib/views/translation_views/translation_views.views.inc

@@ -53,15 +53,16 @@ function translation_views_views_data_alter(array &$data) {
         $pk = $entity_type->getKey('id');
 
         $translations_table['table'] = [
-          'group' => $group,
+          'group'       => $group,
           'entity type' => $entity_type_id,
-          'join' => [
+          'join'        => [
             $field_data_table => [
-              'join_id' => 'translation_views_language_join',
-              'type' => 'LEFT',
+              'join_id'    => 'translation_views_language_join',
+              'entity_pk'  => $pk,
+              'type'       => 'LEFT',
               'left_field' => $pk,
-              'field' => $pk,
-              'table' => $field_data_table,
+              'field'      => $pk,
+              'table'      => $field_data_table,
             ],
           ],
         ];
@@ -73,13 +74,13 @@ function translation_views_views_data_alter(array &$data) {
 
         $translations_table_target['table'] = [
           'group' => $group,
-          'join' => [
+          'join'  => [
             $field_data_table => [
-              'type' => 'LEFT',
+              'type'       => 'LEFT',
               'left_field' => $pk,
-              'field' => $pk,
-              'table' => $field_data_table,
-              'extra' => [
+              'field'      => $pk,
+              'table'      => $field_data_table,
+              'extra'      => [
                 [
                   'field' => 'langcode',
                   'value' => '***TRANSLATION_VIEWS_TARGET_LANG***',
@@ -90,16 +91,16 @@ function translation_views_views_data_alter(array &$data) {
         ];
 
         $translations_table_target += FieldDefinitions::buildOutdatedField(
-          $translations_table['content_translation_outdated']
+          $data[$field_data_table]['content_translation_outdated']
         );
         $translations_table_target += FieldDefinitions::buildSourceField(
-          $translations_table['content_translation_source']
+          $data[$field_data_table]['content_translation_source']
         );
         $translations_table_target += FieldDefinitions::buildDefaultLangcodeField(
-          $translations_table['default_langcode']
+          $data[$field_data_table]['default_langcode']
         );
         $translations_table_target += FieldDefinitions::buildChangedField(
-          $translations_table['changed']
+          $data[$field_data_table]['changed']
         );
       }
     }

+ 6 - 0
sites/all/modules/contrib/views/views_bulk_edit/README.txt

@@ -27,3 +27,9 @@ Configuration
 2. Add a "Views bulk operations" field (global), available on
    all entity types, if not already added.
 3. Check the "Modify field values" action.
+4. (Optional) Define a "bulk_edit" form mode for the entity bundles
+   that you wish to be able to bulk edit. Under the "Form modes" page
+   at /admin/structure/display-modes/form you can add a new form
+   mode. If it uses the machine name "bulk_edit", the form for the
+   "Modify field values" action will use that form mode to determine
+   what fields are available for bulk editing.

+ 2 - 2
sites/all/modules/contrib/views/views_bulk_edit/css/edit_form.css

@@ -1,5 +1,5 @@
-.views-bulk-edit-form .form-type-checkbox,
-.views-bulk-edit-form .js-form-type-checkbox {
+.views-bulk-edit-form .vbe-selector-fieldset .form-type-checkbox,
+.views-bulk-edit-form .vbe-selector-fieldset .js-form-type-checkbox {
   float: left;
   margin-right: 20px;
 }

+ 45 - 4
sites/all/modules/contrib/views/views_bulk_edit/src/Plugin/Action/ModifyEntityValues.php

@@ -109,9 +109,26 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
     $form['#attributes']['class'] = ['views-bulk-edit-form'];
     $form['#attached']['library'][] = 'views_bulk_edit/views_bulk_edit.edit_form';
 
+    $form['options'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Options'),
+    ];
+    $form['options']['_add_values'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Add values to multi-value fields'),
+      '#description' => $this->t('New values of multi-value fields will be added to the existing ones instead of overwriting them.'),
+    ];
+
+    $bundle_count = 0;
     foreach ($bundle_data as $entity_type_id => $bundles) {
       foreach ($bundles as $bundle => $label) {
-        $form = $this->getBundleForm($entity_type_id, $bundle, $label, $form, $form_state);
+        $bundle_count++;
+      }
+    }
+
+    foreach ($bundle_data as $entity_type_id => $bundles) {
+      foreach ($bundles as $bundle => $label) {
+        $form = $this->getBundleForm($entity_type_id, $bundle, $label, $form, $form_state, $bundle_count);
       }
     }
 
@@ -251,11 +268,13 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
    *   Form array.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The form_state object.
+   * @param int $bundle_count
+   *   Number of bundles that may be affected.
    *
    * @return array
    *   Edit form for the current entity bundle.
    */
-  protected function getBundleForm($entity_type_id, $bundle, $bundle_label, array $form, FormStateInterface $form_state) {
+  protected function getBundleForm($entity_type_id, $bundle, $bundle_label, array $form, FormStateInterface $form_state, $bundle_count) {
     $entityType = $this->entityTypeManager->getDefinition($entity_type_id);
     $entity = $this->entityTypeManager->getStorage($entity_type_id)->create([
       $entityType->getKey('bundle') => $bundle,
@@ -273,7 +292,8 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
       $bundle_label = $entityType->getLabel();
     }
     $form[$entity_type_id][$bundle] = [
-      '#type' => 'fieldset',
+      '#type' => 'details',
+      '#open' => ($bundle_count === 1),
       '#title' => $entityType->getLabel() . ' - ' . $bundle_label,
       '#parents' => [$entity_type_id, $bundle],
     ];
@@ -308,6 +328,7 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
       '#title' => $this->t('Select fields to change'),
       '#weight' => -50,
       '#tree' => TRUE,
+      '#attributes' => ['class' => ['vbe-selector-fieldset']],
     ];
 
     foreach (Element::children($form) as $key) {
@@ -325,6 +346,7 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
       $selector['_field_selector'][$key] = [
         '#type' => 'checkbox',
         '#title' => $element['#title'],
+        '#weight' => isset($form[$key]['#weight']) ? $form[$key]['#weight'] : 0,
         '#tree' => TRUE,
       ];
 
@@ -418,6 +440,8 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
         }
       }
     }
+
+    $this->configuration['_add_values'] = $form_state->getValue('_add_values');
   }
 
   /**
@@ -430,10 +454,27 @@ class ModifyEntityValues extends ViewsBulkOperationsActionBase implements Contai
     $result = $this->t('Skip (field is not present on this bundle)');
     if (isset($this->configuration[$type_id][$bundle])) {
       foreach ($this->configuration[$type_id][$bundle] as $field => $value) {
+        if (!empty($this->configuration['_add_values'])) {
+          /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $storageDefinition */
+          $storageDefinition = $entity->{$field}->getFieldDefinition()->getFieldStorageDefinition();
+          $cardinality = $storageDefinition->getCardinality();
+          if ($cardinality === $storageDefinition::CARDINALITY_UNLIMITED || $cardinality > 1) {
+            $current_value = $entity->{$field}->getValue();
+            $value_count = count($current_value);
+            foreach ($value as $item) {
+              if ($cardinality != $storageDefinition::CARDINALITY_UNLIMITED && $value_count >= $cardinality) {
+                break;
+              }
+              $current_value[] = $item;
+            }
+            $value = $current_value;
+          }
+        }
+
         $entity->{$field}->setValue($value);
       }
       $entity->save();
-      $result = $this->t('Modify filed values');
+      $result = $this->t('Modify field values');
     }
     return $result;
   }

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_edit/views_bulk_edit.info.yml

@@ -6,8 +6,8 @@ package: 'Views Bulk Operations'
 dependencies:
   - drupal:views_bulk_operations
 
-# Information added by Drupal.org packaging script on 2018-03-12
-version: '8.x-2.0-rc1'
+# Information added by Drupal.org packaging script on 2018-07-27
+version: '8.x-2.2'
 core: '8.x'
 project: 'views_bulk_edit'
-datestamp: 1520840585
+datestamp: 1532689107

+ 4 - 1
sites/all/modules/contrib/views/views_bulk_operations/js/frontUi.js

@@ -60,7 +60,10 @@
      * @param {bool} state
      * @param {string} value
      */
-    update: function (state, index, value = null) {
+    update: function (state, index, value) {
+      if (value === undefined) {
+        value = null;
+      }
       if (this.view_id.length && this.display_id.length) {
         var list = {};
         if (value && value != 'on') {

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/modules/actions_permissions/actions_permissions.info.yml

@@ -6,8 +6,8 @@ package: 'Views Bulk Operations'
 dependencies:
   - drupal:views_bulk_operations
 
-# Information added by Drupal.org packaging script on 2018-03-10
-version: '8.x-2.1'
+# Information added by Drupal.org packaging script on 2018-07-02
+version: '8.x-2.4'
 core: '8.x'
 project: 'views_bulk_operations'
-datestamp: 1520668088
+datestamp: 1530516826

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/modules/views_bulk_operations_example/views_bulk_operations_example.info.yml

@@ -6,8 +6,8 @@ package: 'Examples'
 dependencies:
   - drupal:views_bulk_operations
 
-# Information added by Drupal.org packaging script on 2018-03-10
-version: '8.x-2.1'
+# Information added by Drupal.org packaging script on 2018-07-02
+version: '8.x-2.4'
 core: '8.x'
 project: 'views_bulk_operations'
-datestamp: 1520668088
+datestamp: 1530516826

+ 2 - 2
sites/all/modules/contrib/views/views_bulk_operations/src/Form/ConfigureAction.php

@@ -114,6 +114,7 @@ class ConfigureAction extends FormBase {
         [$this, 'submitForm'],
       ],
     ];
+    $this->addCancelButton($form);
 
     $action = $this->actionManager->createInstance($form_data['action_id']);
 
@@ -121,9 +122,8 @@ class ConfigureAction extends FormBase {
       $action->setContext($form_data);
     }
 
-    $form = $action->buildConfigurationForm($form, $form_state);
-
     $form_state->set('views_bulk_operations', $form_data);
+    $form = $action->buildConfigurationForm($form, $form_state);
 
     return $form;
   }

+ 4 - 3
sites/all/modules/contrib/views/views_bulk_operations/src/Form/ConfirmAction.php

@@ -87,8 +87,6 @@ class ConfirmAction extends FormBase {
       return;
     }
 
-    $form_state->setStorage($form_data);
-
     if (!empty($form_data['entity_labels'])) {
       $form['list'] = [
         '#theme' => 'item_list',
@@ -113,6 +111,9 @@ class ConfirmAction extends FormBase {
         [$this, 'submitForm'],
       ],
     ];
+    $this->addCancelButton($form);
+
+    $form_state->set('views_bulk_operations', $form_data);
 
     return $form;
   }
@@ -121,7 +122,7 @@ class ConfirmAction extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $form_data = $form_state->getStorage();
+    $form_data = $form_state->get('views_bulk_operations');
     $this->deleteTempstoreData($form_data['view_id'], $form_data['display_id']);
     $this->actionProcessor->executeProcessing($form_data);
     $form_state->setRedirectUrl($form_data['redirect_url']);

+ 32 - 0
sites/all/modules/contrib/views/views_bulk_operations/src/Form/ViewsBulkOperationsFormTrait.php

@@ -3,6 +3,7 @@
 namespace Drupal\views_bulk_operations\Form;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Defines common methods for Views Bulk Operations forms.
@@ -162,4 +163,35 @@ trait ViewsBulkOperationsFormTrait {
     return $this->getTempstore($view_id, $display_id)->delete($this->currentUser()->id());
   }
 
+  /**
+   * Add a cancel button into a VBO form.
+   *
+   * @param array $form
+   *   The form definition.
+   */
+  protected function addCancelButton(array &$form) {
+    $form['actions']['cancel'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Cancel'),
+      '#submit' => [
+        [$this, 'cancelForm'],
+      ],
+      '#limit_validation_errors' => [],
+    ];
+  }
+
+  /**
+   * Submit callback to cancel an action and return to the view.
+   *
+   * @param array $form
+   *   The form definition.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   */
+  public function cancelForm(array &$form, FormStateInterface $form_state) {
+    $form_data = $form_state->get('views_bulk_operations');
+    drupal_set_message($this->t('Canceled "%action".', ['%action' => $form_data['action_label']]));
+    $form_state->setRedirectUrl($form_data['redirect_url']);
+  }
+
 }

+ 10 - 7
sites/all/modules/contrib/views/views_bulk_operations/src/Plugin/views/field/ViewsBulkOperationsBulkForm.php

@@ -71,11 +71,11 @@ class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDe
   protected $currentUser;
 
   /**
-   * The current URL object.
+   * The request stack.
    *
-   * @var \Drupal\Core\Url
+   * @var \Symfony\Component\HttpFoundation\RequestStack
    */
-  protected $url;
+  protected $requestStack;
 
   /**
    * An array of actions that can be executed.
@@ -141,7 +141,7 @@ class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDe
     $this->actionProcessor = $actionProcessor;
     $this->tempStoreFactory = $tempStoreFactory;
     $this->currentUser = $currentUser;
-    $this->url = Url::createFromRequest($requestStack->getCurrentRequest());
+    $this->requestStack = $requestStack;
   }
 
   /**
@@ -212,7 +212,7 @@ class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDe
       'batch_size' => $this->options['batch'] ? $this->options['batch_size'] : 0,
       'total_results' => $this->viewData->getTotalResults(),
       'arguments' => $this->view->args,
-      'redirect_url' => $this->url,
+      'redirect_url' => Url::createFromRequest(clone $this->requestStack->getCurrentRequest()),
       'exposed_input' => $this->view->getExposedInput(),
     ];
 
@@ -540,7 +540,6 @@ class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDe
       // Update tempstore data.
       $this->updateTempstoreData();
 
-      $use_revision = array_key_exists('revision', $this->view->getQuery()->getEntityTableInfo());
       $form[$this->options['id']]['#tree'] = TRUE;
 
       // Get pager data if available.
@@ -763,11 +762,15 @@ class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDe
       $this->tempStoreData['preconfiguration'] = isset($this->options['preconfiguration'][$action_id]) ? $this->options['preconfiguration'][$action_id] : [];
 
       if (!$form_state->getValue('select_all')) {
-        // Update list data with the form selection.
+
+        // Update list data with the current form selection.
         foreach ($form_state->getValue($this->options['id']) as $row_index => $bulkFormKey) {
           if ($bulkFormKey) {
             $this->tempStoreData['list'][$bulkFormKey] = $this->getListItem($bulkFormKey, $form[$this->options['id']][$row_index]['#title']);
           }
+          else {
+            unset($this->tempStoreData['list'][$form[$this->options['id']][$row_index]['#return_value']]);
+          }
         }
       }
       else {

+ 2 - 2
sites/all/modules/contrib/views/views_bulk_operations/src/Service/ViewsBulkOperationsViewData.php

@@ -193,7 +193,7 @@ class ViewsBulkOperationsViewData implements ViewsBulkOperationsViewDataInterfac
       throw new \Exception('Unexpected view result row structure.');
     }
 
-    if ($entity->isTranslatable()) {
+    if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
 
       // Try to find a field alias for the langcode.
       // Assumption: translatable entities always
@@ -216,7 +216,7 @@ class ViewsBulkOperationsViewData implements ViewsBulkOperationsViewDataInterfac
         $language_field = $langcode_key;
       }
 
-      if ($entity instanceof TranslatableInterface && isset($row->{$language_field})) {
+      if (isset($row->{$language_field})) {
         return $entity->getTranslation($row->{$language_field});
       }
     }

+ 1 - 1
sites/all/modules/contrib/views/views_bulk_operations/tests/src/Functional/ViewsBulkOperationsBulkFormTest.php

@@ -32,7 +32,7 @@ class ViewsBulkOperationsBulkFormTest extends BrowserTestBase {
     $this->drupalCreateContentType(['type' => 'page']);
 
     $this->testNodes = [];
-    $time = REQUEST_TIME;
+    $time = $this->container->get('datetime.time')->getRequestTime();
     for ($i = 0; $i < 15; $i++) {
       // Ensure nodes are sorted in the same order they are inserted in the
       // array.

+ 3 - 2
sites/all/modules/contrib/views/views_bulk_operations/tests/src/Kernel/ViewsBulkOperationsKernelTestBase.php

@@ -101,7 +101,8 @@ abstract class ViewsBulkOperationsKernelTestBase extends KernelTestBase {
       'language',
     ]);
 
-    // Get VBO view data service.
+    // Get time and VBO view data services.
+    $this->time = $this->container->get('datetime.time');
     $this->vboDataService = $this->container->get('views_bulk_operations.data');
   }
 
@@ -133,7 +134,7 @@ abstract class ViewsBulkOperationsKernelTestBase extends KernelTestBase {
       }
 
       // Create some test nodes.
-      $time = REQUEST_TIME;
+      $time = $this->time->getRequestTime();
       if (!isset($type_data['count'])) {
         $type_data['count'] = 10;
       }

+ 0 - 3
sites/all/modules/contrib/views/views_bulk_operations/tests/src/Unit/ViewsBulkOperationsBatchTest.php

@@ -39,9 +39,6 @@ class ViewsBulkOperationsBatchTest extends UnitTestCase {
     $actionProcessor = $this->getMockBuilder('Drupal\views_bulk_operations\Service\ViewsBulkOperationsActionProcessor')
       ->disableOriginalConstructor()
       ->getMock();
-    $actionProcessor->expects($this->any())
-      ->method('getEntity')
-      ->will($this->returnValue(new \stdClass()));
 
     $actionProcessor->expects($this->any())
       ->method('populateQueue')

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/tests/views_bulk_operations_test/views_bulk_operations_test.info.yml

@@ -7,8 +7,8 @@ dependencies:
   - drupal:views_bulk_operations
   - drupal:node
 
-# Information added by Drupal.org packaging script on 2018-03-10
-version: '8.x-2.1'
+# Information added by Drupal.org packaging script on 2018-07-02
+version: '8.x-2.4'
 core: '8.x'
 project: 'views_bulk_operations'
-datestamp: 1520668088
+datestamp: 1530516826

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.info.yml

@@ -6,8 +6,8 @@ package: 'Views'
 dependencies:
   - drupal:views (>=8.4)
 
-# Information added by Drupal.org packaging script on 2018-03-10
-version: '8.x-2.1'
+# Information added by Drupal.org packaging script on 2018-07-02
+version: '8.x-2.4'
 core: '8.x'
 project: 'views_bulk_operations'
-datestamp: 1520668088
+datestamp: 1530516826