Browse Source

security updates of unpatched modules

Bachir Soussi Chiadmi 7 years ago
parent
commit
f6f7fd575f
100 changed files with 3984 additions and 2063 deletions
  1. 42 17
      sites/all/modules/contrib/admin/google_analytics/README.txt
  2. 12 0
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc
  3. 9 0
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js
  4. 47 17
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js
  5. 3 3
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.info
  6. 5 4
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.install
  7. 44 14
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.js
  8. 29 7
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.module
  9. 56 6
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.test
  10. 4 0
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc
  11. 14 1
      sites/all/modules/contrib/dev/elysia_cron/API.txt
  12. 10 7
      sites/all/modules/contrib/dev/elysia_cron/INSTALL.txt
  13. 73 48
      sites/all/modules/contrib/dev/elysia_cron/README.txt
  14. 7 7
      sites/all/modules/contrib/dev/elysia_cron/cron.php
  15. 0 13
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron-multilingual-image-path-2096571-1.patch
  16. 322 196
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.admin.inc
  17. 134 0
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.api.php
  18. 130 61
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.ctools.inc
  19. 86 82
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.drush.inc
  20. 5 5
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.info
  21. 83 34
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.install
  22. 548 391
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron.module
  23. 297 89
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron_scheduler.inc
  24. 0 230
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron_scheduler_old.inc
  25. 0 327
      sites/all/modules/contrib/dev/elysia_cron/elysia_cron_update.php
  26. 0 86
      sites/all/modules/contrib/dev/elysia_cron/elysia_drupalconv.php
  27. 30 28
      sites/all/modules/contrib/flag/flag/flag.api.php
  28. 1 1
      sites/all/modules/contrib/flag/flag/flag.flag.inc
  29. 3 3
      sites/all/modules/contrib/flag/flag/flag.info
  30. 9 37
      sites/all/modules/contrib/flag/flag/flag.install
  31. 116 46
      sites/all/modules/contrib/flag/flag/flag.module
  32. 7 2
      sites/all/modules/contrib/flag/flag/flag.tokens.inc
  33. 3 5
      sites/all/modules/contrib/flag/flag/flag_actions.info
  34. 14 14
      sites/all/modules/contrib/flag/flag/flag_actions.module
  35. 4 3
      sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.info
  36. 21 0
      sites/all/modules/contrib/flag/flag/flag_bookmark/includes/flag_bookmark.views.inc
  37. 4 3
      sites/all/modules/contrib/flag/flag/flag_bookmark/includes/flag_bookmark.views_default.inc
  38. 89 0
      sites/all/modules/contrib/flag/flag/flag_bookmark/plugins/flag_bookmark_plugin_validate_user.inc
  39. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag.actions.inc
  40. 12 5
      sites/all/modules/contrib/flag/flag/includes/flag.admin.inc
  41. 3 3
      sites/all/modules/contrib/flag/flag/includes/flag.export.inc
  42. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag.features.inc
  43. 3 3
      sites/all/modules/contrib/flag/flag/includes/flag.pages.inc
  44. 80 0
      sites/all/modules/contrib/flag/flag/includes/flag/flag_comment.inc
  45. 59 45
      sites/all/modules/contrib/flag/flag/includes/flag/flag_flag.inc
  46. 2 2
      sites/all/modules/contrib/flag/flag/includes/flag/flag_node.inc
  47. 21 0
      sites/all/modules/contrib/flag/flag/includes/views/flag.views.inc
  48. 1 1
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_argument_entity_id.inc
  49. 128 2
      sites/all/modules/contrib/flag/flag/tests/flag.test
  50. 13 0
      sites/all/modules/contrib/flag/flag/tests/flag_comment_flag_test/flag_comment_flag_test.info
  51. 41 0
      sites/all/modules/contrib/flag/flag/tests/flag_comment_flag_test/flag_comment_flag_test.module
  52. 3 3
      sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.info
  53. 3 3
      sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.info
  54. 2 2
      sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.module
  55. 3 3
      sites/all/modules/contrib/flag/flag/tests/flagaccesstest/flagaccesstest.info
  56. 18 23
      sites/all/modules/contrib/form/webform/includes/webform.submissions.inc
  57. 3 3
      sites/all/modules/contrib/form/webform/webform.info
  58. 15 1
      sites/all/modules/contrib/form/webform/webform.module
  59. 2 5
      sites/all/modules/contrib/panels/panels/README.txt
  60. 11 1
      sites/all/modules/contrib/panels/panels/UPGRADE.txt
  61. 0 5
      sites/all/modules/contrib/panels/panels/css/panels.css
  62. 14 2
      sites/all/modules/contrib/panels/panels/css/panels_dnd.css
  63. 1 1
      sites/all/modules/contrib/panels/panels/help/plugins-layout.html
  64. 3 3
      sites/all/modules/contrib/panels/panels/i18n_panels/i18n_panels.info
  65. 2 0
      sites/all/modules/contrib/panels/panels/includes/add-content.inc
  66. 29 11
      sites/all/modules/contrib/panels/panels/includes/common.inc
  67. 2 2
      sites/all/modules/contrib/panels/panels/includes/display-edit.inc
  68. 2 0
      sites/all/modules/contrib/panels/panels/includes/display-layout.inc
  69. 26 1
      sites/all/modules/contrib/panels/panels/includes/plugins.inc
  70. 39 0
      sites/all/modules/contrib/panels/panels/js/panels-base.js
  71. 12 5
      sites/all/modules/contrib/panels/panels/panels.info
  72. 198 1
      sites/all/modules/contrib/panels/panels/panels.install
  73. 104 18
      sites/all/modules/contrib/panels/panels/panels.module
  74. 18 6
      sites/all/modules/contrib/panels/panels/panels_ipe/js/panels_ipe.js
  75. 31 0
      sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.api.php
  76. 3 4
      sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.info
  77. 5 2
      sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.module
  78. 70 3
      sites/all/modules/contrib/panels/panels/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php
  79. 3 4
      sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.info
  80. 59 1
      sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.install
  81. 76 13
      sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.module
  82. 8 3
      sites/all/modules/contrib/panels/panels/panels_mini/plugins/content_types/panels_mini.inc
  83. 22 0
      sites/all/modules/contrib/panels/panels/panels_mini/plugins/panels_storage/panels_mini.inc
  84. 3 4
      sites/all/modules/contrib/panels/panels/panels_node/panels_node.info
  85. 96 0
      sites/all/modules/contrib/panels/panels/panels_node/panels_node.install
  86. 36 40
      sites/all/modules/contrib/panels/panels/panels_node/panels_node.module
  87. 25 0
      sites/all/modules/contrib/panels/panels/panels_node/plugins/panels_storage/panels_node.inc
  88. 45 5
      sites/all/modules/contrib/panels/panels/plugins/display_renderers/panels_renderer_editor.class.php
  89. 44 2
      sites/all/modules/contrib/panels/panels/plugins/display_renderers/panels_renderer_standard.class.php
  90. 5 0
      sites/all/modules/contrib/panels/panels/plugins/page_wizards/landing_page.inc
  91. 19 0
      sites/all/modules/contrib/panels/panels/plugins/panels_storage/page_manager.inc
  92. 4 4
      sites/all/modules/contrib/panels/panels/plugins/styles/stylizer.inc
  93. 33 24
      sites/all/modules/contrib/panels/panels/plugins/task_handlers/panel_context.inc
  94. 6 0
      sites/all/modules/contrib/panels/panels/templates/panels-add-content-modal.tpl.php
  95. 103 0
      sites/all/modules/contrib/panels/panels/tests/PanelsEntityViewWebTestCase.test
  96. 31 0
      sites/all/modules/contrib/panels/panels/tests/PanelsNodeViewWebTestCase.test
  97. 31 0
      sites/all/modules/contrib/panels/panels/tests/PanelsTermViewWebTestCase.test
  98. 31 0
      sites/all/modules/contrib/panels/panels/tests/PanelsUserViewWebTestCase.test
  99. 49 2
      sites/all/modules/contrib/search/search_api/CHANGELOG.txt
  100. 10 3
      sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/adapter.inc

+ 42 - 17
sites/all/modules/contrib/admin/google_analytics/README.txt

@@ -12,12 +12,20 @@ Requirements
 
 * Google Analytics user account
 
-
 Installation
 ============
 Copy the 'googleanalytics' module directory in to your Drupal
 sites/all/modules directory as usual.
 
+Upgrading from 6.x-3.x and 7.x-1.x
+==================================
+If you upgrade from 6.x-3.x and 7.x-1.x (ga.js) to 7.x-2.x (analytics.js) you
+should verify if you used custom variables. Write down your settings or make a 
+screenshot. You need to re-configure the settings to use custom dimensions or
+metrics. There is no automatic upgrade path for custom variables feature. All
+other module settings are upgraded automatically.
+
+See https://support.google.com/analytics/answer/2795983?hl=en for more details.
 
 Usage
 =====
@@ -27,12 +35,8 @@ All pages will now have the required JavaScript added to the
 HTML footer can confirm this by viewing the page source from
 your browser.
 
-New approach to page tracking in 5.x-1.5 and 6.x-1.1
-====================================================
-With 5.x-1.5 and 6.x-1.1 there are new settings on the settings page at
-admin/config/system/googleanalytics. The "Page specific tracking" area now
-comes with an interface that copies Drupal's block visibility settings.
-
+Page specific tracking
+======================
 The default is set to "Add to every page except the listed pages". By
 default the following pages are listed for exclusion:
 
@@ -44,23 +48,24 @@ node/*/*
 user/*/*
 
 These defaults are changeable by the website administrator or any other
-user with 'administer google analytics' permission.
+user with 'Administer Google Analytics' permission.
 
-Like the blocks visibility settings in Drupal core, there is now a
-choice for "Add if the following PHP code returns TRUE." Sample PHP snippets
-that can be used in this textarea can be found on the handbook page
-"Overview-approach to block visibility" at http://drupal.org/node/64135.
+Like the blocks visibility settings in Drupal core, there is a choice for
+"Add if the following PHP code returns TRUE." Sample PHP snippets that can be
+used in this textarea can be found on the handbook page "Overview-approach to
+block visibility" at http://drupal.org/node/64135.
 
 Custom dimensions and metrics
 =============================
 One example for custom dimensions tracking is the "User roles" tracking.
 
-1. In the Google Analytics Management Interface you need to setup Dimension #1
-   with name e.g. "User roles". This step is required. Do not miss it, please.
+1. In the Google Analytics Management Interface (http://www.google.com/analytics/)
+   you need to setup Dimension #1 with name e.g. "User roles". This step is
+   required. Do not miss it, please.
 
-2. Enter the below configuration data into the custom dimensions settings form
-   under admin/config/system/googleanalytics. You can also choose another index,
-   but keep it always in sync with the index used in step #1.
+2. Enter the below configuration data into the Drupal custom dimensions settings
+   form under admin/config/system/googleanalytics. You can also choose another
+   index, but keep it always in sync with the index used in step #1.
 
    Index: 1
    Value: [current-user:role-names]
@@ -77,3 +82,23 @@ provided for any customisations you include.
 
 To speed up page loading you may also cache the Google Analytics "analytics.js"
 file locally.
+
+Manual JS debugging
+===================
+For manual debugging of the JS code you are able to create a test node. This
+is the example HTML code for this test node. You need to enable debugging mode
+in your Drupal configuration of Google Analytics settings to see verbose
+messages in your browsers JS console.
+
+Title: Google Analytics test page
+
+Body:
+<ul>
+  <li><a href="mailto:foo@example.com">Mailto</a></li>
+  <li><a href="/files/test.txt">Download file</a></li>
+  <li><a class="colorbox" href="#">Open colorbox</a></li>
+  <li><a href="http://example.com/">External link</a></li>
+  <li><a href="/go/test">Go link</a></li>
+</ul>
+
+Text format: Full HTML

File diff suppressed because it is too large
+ 12 - 0
sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc


+ 9 - 0
sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js

@@ -65,6 +65,15 @@ Drupal.behaviors.trackingSettingsSummary = {
       if ($('input#edit-googleanalytics-trackfiles', context).is(':checked')) {
         vals.push(Drupal.t('Downloads'));
       }
+      if ($('input#edit-googleanalytics-trackcolorbox', context).is(':checked')) {
+        vals.push(Drupal.t('Colorbox'));
+      }
+      if ($('input#edit-googleanalytics-tracklinkid', context).is(':checked')) {
+        vals.push(Drupal.t('Link attribution'));
+      }
+      if ($('input#edit-googleanalytics-trackurlfragments', context).is(':checked')) {
+        vals.push(Drupal.t('URL fragments'));
+      }
       if (!vals.length) {
         return Drupal.t('Not tracked');
       }

+ 47 - 17
sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js

@@ -8,16 +8,16 @@ $(document).ready(function() {
   // clicks on all elements.
   $(document.body).bind("mousedown keyup touchstart", function(event) {
     console.group("Running Google Analytics for Drupal.");
-    console.info(event);
+    console.info("Event '%s' has been detected.", event.type);
 
     // Catch the closest surrounding link of a clicked element.
     $(event.target).closest("a,area").each(function() {
-      console.info("Element '%o' has been detected. Link '%s' found.", this, this.href);
+      console.info("Closest element '%o' has been found. URL '%s' extracted.", this, this.href);
 
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           console.info("Click on .colorbox item has been detected.");
         }
@@ -25,12 +25,22 @@ $(document).ready(function() {
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
           console.info("Download url '%s' has been found. Tracked download as extension '%s'.", Drupal.googleanalytics.getPageUrl(this.href), Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase());
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
           console.info("Click on internal special link '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(this.href));
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else {
           // e.g. anchor in same page or other internal page link
@@ -41,13 +51,25 @@ $(document).ready(function() {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
           console.info("Click on e-mail '%s' has been tracked.", this.href.substring(7));
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
             console.info("Outbound link '%s' has been tracked.", this.href);
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
           else {
             console.info("Internal link '%s' clicked, not tracked.", this.href);
@@ -63,19 +85,27 @@ $(document).ready(function() {
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
       console.info("Track URL '%s' as pageview. Hash '%s' has changed.", location.pathname + location.search + location.hash, location.hash);
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 

+ 3 - 3
sites/all/modules/contrib/admin/google_analytics/googleanalytics.info

@@ -5,9 +5,9 @@ package = Statistics
 configure = admin/config/system/googleanalytics
 files[] = googleanalytics.test
 test_dependencies[] = token
-; Information added by Drupal.org packaging script on 2014-11-29
-version = "7.x-2.1"
+; Information added by Drupal.org packaging script on 2016-08-09
+version = "7.x-2.3"
 core = "7.x"
 project = "google_analytics"
-datestamp = "1417276982"
+datestamp = "1470779953"
 

+ 5 - 4
sites/all/modules/contrib/admin/google_analytics/googleanalytics.install

@@ -25,6 +25,7 @@ function googleanalytics_uninstall() {
   variable_del('googleanalytics_roles');
   variable_del('googleanalytics_site_search');
   variable_del('googleanalytics_trackadsense');
+  variable_del('googleanalytics_trackcolorbox');
   variable_del('googleanalytics_trackdoubleclick');
   variable_del('googleanalytics_tracker_anonymizeip');
   variable_del('googleanalytics_trackfiles');
@@ -446,16 +447,16 @@ function googleanalytics_update_7200() {
   if (!empty($googleanalytics_codesnippet_before) && stristr($googleanalytics_codesnippet_before, '_gaq.push(')) {
     variable_set('googleanalytics_codesnippet_before_backup_7200', $googleanalytics_codesnippet_before);
     variable_del('googleanalytics_codesnippet_before');
-    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning');
-    $messages[] = t('Manual upgrade of custom "before" code snippet is required.');
+    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet to analytics.js API."), 'warning');
+    $messages[] = t('Manual upgrade of custom "before" code snippet from ja.js to analytics.js API is required.');
   }
 
   $googleanalytics_codesnippet_after = variable_get('googleanalytics_codesnippet_after', '');
   if (!empty($googleanalytics_codesnippet_after) && stristr($googleanalytics_codesnippet_after, '_gaq.push(')) {
     variable_set('googleanalytics_codesnippet_after_backup_7200', $googleanalytics_codesnippet_after);
     variable_del('googleanalytics_codesnippet_after');
-    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning');
-    $messages[] = t('Manual upgrade of custom "after" code snippet is required.');
+    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_after_backup_7200'. You need to manually upgrade the custom 'after' code snippet to analytics.js API."), 'warning');
+    $messages[] = t('Manual upgrade of custom "after" code snippet from ja.js to analytics.js API is required.');
   }
 
   return empty($messages) ? t('No custom code snipped found. Nothing to do.') : implode(' ', $messages);

+ 44 - 14
sites/all/modules/contrib/admin/google_analytics/googleanalytics.js

@@ -14,29 +14,51 @@ $(document).ready(function() {
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           //console.info("Click on .colorbox item has been detected.");
         }
         // Is download tracking activated and the file extension configured for download tracking?
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
       }
       else {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
         }
       }
@@ -46,18 +68,26 @@ $(document).ready(function() {
   // Track hash changes as unique pageviews, if this option has been enabled.
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 

+ 29 - 7
sites/all/modules/contrib/admin/google_analytics/googleanalytics.module

@@ -69,6 +69,11 @@ function googleanalytics_permission() {
       'description' => t('Enter PHP code in the field for tracking visibility settings.'),
       'restrict access' => TRUE,
     ),
+    'add JS snippets for google analytics' => array(
+      'title' => t('Add JavaScript snippets'),
+      'description' => 'Enter JavaScript code snippets for advanced Google Analytics functionality.',
+      'restrict access' => TRUE,
+    ),
   );
 }
 
@@ -125,6 +130,9 @@ function googleanalytics_page_alter(&$page) {
       $link_settings['trackDownload'] = $track_download;
       $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
     }
+    if (module_exists('colorbox') && ($track_colorbox = variable_get('googleanalytics_trackcolorbox', 1))) {
+      $link_settings['trackColorbox'] = $track_colorbox;
+    }
     if ($track_domain_mode = variable_get('googleanalytics_domain_mode', 0)) {
       $link_settings['trackDomainMode'] = $track_domain_mode;
     }
@@ -294,10 +302,7 @@ function googleanalytics_page_alter(&$page) {
 
     // Track logged in users across all devices.
     if (variable_get('googleanalytics_trackuserid', 0) && user_is_logged_in()) {
-      // The USER_ID value should be a unique, persistent, and non-personally
-      // identifiable string identifier that represents a user or signed-in
-      // account across devices.
-      $create_only_fields['userId'] = drupal_hmac_base64($user->uid, drupal_get_private_key() . drupal_get_hash_salt());
+      $create_only_fields['userId'] = google_analytics_user_id_hash($user->uid);
     }
 
     // Create a tracker.
@@ -352,13 +357,30 @@ function googleanalytics_page_alter(&$page) {
       // Custom tracking. Prepend before all other JavaScript.
       // @TODO: https://support.google.com/adsense/answer/98142
       // sounds like it could be appended to $script.
-      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1));
+      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
     }
 
-    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline'));
+    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline', 'requires_jquery' => FALSE));
   }
 }
 
+/**
+ * Generate user id hash to implement USER_ID.
+ *
+ * The USER_ID value should be a unique, persistent, and non-personally
+ * identifiable string identifier that represents a user or signed-in
+ * account across devices.
+ *
+ * @param int $uid
+ *   User id.
+ *
+ * @return string
+ *   User id hash.
+ */
+function google_analytics_user_id_hash($uid) {
+  return drupal_hmac_base64($uid, drupal_get_private_key() . drupal_get_hash_salt());
+}
+
 /**
  * Implements hook_field_extra_fields().
  */
@@ -456,7 +478,7 @@ function googleanalytics_preprocess_search_results(&$variables) {
     // found. But the pager item mumber can tell the number of search results.
     global $pager_total_items;
 
-    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1));
+    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
   }
 }
 

+ 56 - 6
sites/all/modules/contrib/admin/google_analytics/googleanalytics.test

@@ -6,6 +6,13 @@
  */
 class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
 
+  /**
+   * User without permissions to edit snippets.
+   *
+   * @var \StdClass
+   */
+  protected $noSnippetUser;
+
   public static function getInfo() {
     return array(
       'name' => 'Google Analytics basic tests',
@@ -25,6 +32,8 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     );
 
     // User to set up google_analytics.
+    $this->noSnippetUser = $this->drupalCreateUser($permissions);
+    $permissions[] = 'add JS snippets for google analytics';
     $this->admin_user = $this->drupalCreateUser($permissions);
     $this->drupalLogin($this->admin_user);
   }
@@ -48,6 +57,26 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $edit['googleanalytics_account'] = $this->randomName(2);
     $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
     $this->assertRaw(t('A valid Google Analytics Web Property ID is case sensitive and formatted like UA-xxxxxxx-yy.'), '[testGoogleAnalyticsConfiguration]: Invalid Web Property ID number validated.');
+
+    // User should have access to code snippets.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is enabled.');
+
+    // Login as user without JS permissions.
+    $this->drupalLogin($this->noSnippetUser);
+    $this->drupalGet('admin/config/system/googleanalytics');
+
+    // User should *not* have access to snippets, but create fields.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is disabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is disabled.');
   }
 
   function testGoogleAnalyticsPageVisibility() {
@@ -284,6 +313,7 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
 
     // User to set up google_analytics.
     $this->admin_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->admin_user);
   }
 
   function testGoogleAnalyticsCustomDimensions() {
@@ -362,34 +392,30 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
       1 => array(
         'index' => 1,
         'value' => '6',
-        'value_expected' => 6,
       ),
       2 => array(
         'index' => 2,
         'value' => '8000',
-        'value_expected' => 8000,
       ),
       3 => array(
         'index' => 3,
         'value' => '7.8654',
-        'value_expected' => 7.8654,
       ),
       4 => array(
         'index' => 4,
         'value' => '1123.4',
-        'value_expected' => 1123.4,
       ),
       5 => array(
         'index' => 5,
         'value' => '5,67',
-        'value_expected' => 5,
       ),
     );
+
     variable_set('googleanalytics_custom_metric', $googleanalytics_custom_metric);
     $this->drupalGet('');
 
     foreach ($googleanalytics_custom_metric as $metric) {
-      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode($metric['value_expected']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
+      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode((float) $metric['value']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
     }
 
     // Test whether tokens are replaced in custom metric values.
@@ -421,6 +447,30 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
     $this->assertNoRaw('ga("set", ' . drupal_json_encode('metric3') . ', ' . drupal_json_encode('') . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Empty value is not shown.');
     $this->assertRaw('ga("set", ' . drupal_json_encode('metric4') . ', ' . drupal_json_encode(0) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Value 0 is shown.');
   }
+
+  /**
+   * Tests if Custom Dimensions token form validation works.
+   */
+  public function testGoogleAnalyticsCustomDimensionsTokenFormValidation() {
+    $ua_code = 'UA-123456-1';
+
+    // Check form validation.
+    $edit['googleanalytics_account'] = $ua_code;
+    $edit['googleanalytics_custom_dimension[indexes][1][value]'] = '[current-user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][2][value]'] = '[current-user:edit-url]';
+    $edit['googleanalytics_custom_dimension[indexes][3][value]'] = '[user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][4][value]'] = '[term:name]';
+    $edit['googleanalytics_custom_dimension[indexes][5][value]'] = '[term:tid]';
+
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 1)), '@invalid-tokens' => implode(', ', array('[current-user:name]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 2)), '@invalid-tokens' => implode(', ', array('[current-user:edit-url]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 3)), '@invalid-tokens' => implode(', ', array('[user:name]')))));
+    // BUG #2037595
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 4)), '@invalid-tokens' => implode(', ', array('[term:name]')))));
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 5)), '@invalid-tokens' => implode(', ', array('[term:tid]')))));
+  }
 }
 
 class GoogleAnalyticsStatusMessagesTest extends DrupalWebTestCase {

+ 4 - 0
sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc

@@ -40,6 +40,10 @@ function googleanalytics_variable_group_info() {
 
 /**
  * Validate Web Property ID variable.
+ *
+ * @param array $variable
+ *
+ * @return string
  */
 function googleanalytics_validate_googleanalytics_account($variable) {
   // Replace all type of dashes (n-dash, m-dash, minus) with the normal dashes.

+ 14 - 1
sites/all/modules/contrib/dev/elysia_cron/API.txt

@@ -126,7 +126,7 @@ You can use the "hook_cron_alter" function to edit cronapi data of other
 modules.
 
 Example:
-function module_cron_alter($data) {
+function module_cron_alter(&$data) {
   $data['key']['rule'] = '0 */6 * * *';
 }
 
@@ -182,6 +182,19 @@ function phptemplate_elysia_cron_description($job) {
 Note: module default theme_elysia_cron_description($job) already contains
 some common tasks descriptions.
 
+-----------------------------------------------------------------------------
+DISABLE CRON JOBS VIA settings.php FILE
+-----------------------------------------------------------------------------
+If you have some instances for the project you can want to disable some cron
+jobs on different instances. For example you don't want to execute PROD cron
+jobs on DEV instance. There is no need to make it via interface or via SQL
+query. You can define variable for each cron job to manage it state. For more
+information please look at `_elysia_cron_is_job_disabled` and `_ec_get_name`
+functions.
+
+For example, if you have cron job with name googleanalytics_cron, you can
+add this string to your settings.php file:
+$conf['ec_googleanalytics_cron_d'] = TRUE;
 
 -----------------------------------------------------------------------------
 OLD 1.x MODULE API (OBSOLETE)

+ 10 - 7
sites/all/modules/contrib/dev/elysia_cron/INSTALL.txt

@@ -16,8 +16,7 @@ up and running.
 You can stop here if you don't need a great precision over task execution and
 you don't have to execute a task more often than once an hour.  
 For example, if you need only the "Once a day", "Once a week" or "Once a month" 
-schedule rules the basic install is fine. (For D6 users that want to stop here: 
-you should have installed Drupal crontab as described in Drupal INSTALL guide).
+schedule rules the basic install is fine.
 
 Instead, if you need:
 - to run some tasks more often than once an hour (eg: you have a function that
@@ -41,8 +40,8 @@ The only difference is that you should use the "* * * * *" rule part instead of
 "0 * * * *" or "45 * * * *" as described in the guide.
 
 While you're editing the system crontab, it's also recommended to replace the
-"/cron.php" part with "/sites/modules/elysia_cron/cron.php" (if you have
-installed elysia_cron in "sites/modules" directory).
+"/cron.php" part with "/sites/all/modules/elysia_cron/cron.php" (if you have
+installed elysia_cron in "sites/all/modules" directory).
 This is an optional step (you can leave "/cron.php" if you want), doing it will 
 result in a better performance in bigger sites (elysia_cron's cron.php handles 
 cache in a better way).
@@ -78,6 +77,10 @@ red; font-weight: bold; }
 PERMISSIONS
 ------------
 
-You can also give 'administer elysia_cron' permission to all user roles that
-needs to administer cron jobs. You can do this with standard drupal users
-administration.
+There are three permission provided by module:
+ * Administer elysia cron - Perform changes to cron jobs timings, disable cron
+ or single jobs and access cron execution statistics;
+ * Execute elysia cron jobs - Allow users to view statistics, execution status
+ and do manually execute cron jobs;
+ * View elysia cron stats - Allows users to view statistics and execution status
+ of cron jobs;

+ 73 - 48
sites/all/modules/contrib/dev/elysia_cron/README.txt

@@ -9,27 +9,27 @@ For module developers API documetation read API.TXT
 FEATURES
 -----------------------------------------------------------------------------
 
-Elysia Cron extends Drupal standard cron, allowing a fine grain control over 
+Elysia Cron extends Drupal standard cron, allowing a fine grain control over
 each task and several ways to add custom cron jobs to your site.
 
 - Set the timings and frequencies of each cron task (you can run some jobs every
   day at a specified hour, other only monthly and so on...).
-  For each task you can simply choose between some frequently used options 
-  ("once a day", "once a month" ...), or use a powerful "linux crontab"-like 
-  syntax to set the accurate timings. You can even define your frequently used 
+  For each task you can simply choose between some frequently used options
+  ("once a day", "once a month" ...), or use a powerful "linux crontab"-like
+  syntax to set the accurate timings. You can even define your frequently used
   options to speed up site configuration.
-- Parallel execution of cron task: you can group jobs in channels and execute 
+- Parallel execution of cron task: you can group jobs in channels and execute
   then simultaneously: so a task that takes a lot of time to execute won't block
   other tasks that need to be executed every 5 minutes...
 - You can disable all tasks, an entire channel or a single task.
 - Change the priority/order of task execution.
 - Manual force the execution of a cron tasks.
-- Detailed overview of cron status with time statistics for single tasks and 
+- Detailed overview of cron status with time statistics for single tasks and
   channels.
 
-- Powerful API for module developers: you can define extra cron tasks for your 
-  modules, each one with own default timings (site administrators can override 
-  them by configuration, other modules via hook_alter). Elysia Cron 2.0 gives a 
+- Powerful API for module developers: you can define extra cron tasks for your
+  modules, each one with own default timings (site administrators can override
+  them by configuration, other modules via hook_alter). Elysia Cron 2.0 gives a
   brand new API support (compatible with 1.0 version) with a lot of features.
 - Administrators can define custom jobs (call to functions with parameters), via
   the "script" option.
@@ -43,7 +43,7 @@ It also can be used in a Drupal install profile.
 3rd party integration:
 - Ping feature, for external tracking services like host-tracker to tell whether
   cron is functioning properly on your site.
-- Drush support: you can call "drush elysia-cron" to manually run extended cron.
+- Drush support: you can call "drush elysia-cron run" to manually run extended cron.
 - CTools support for exports/backup of task settings.
 - Features support.
 
@@ -53,35 +53,35 @@ USAGE EXAMPLES
 
 Elysia cron is usually used in large sites that needs performance optimization.
 
-- Avoid drupal peak loads by distributing heavy load tasks during quiet periods 
-  of the day: for example you may want to rebuild the XML Sitemap of your site 
-  at 2:00AM in the morning, where usually only a few people are visiting your 
-  site. You can even move some tasks to be executed only once a month (log 
+- Avoid drupal peak loads by distributing heavy load tasks during quiet periods
+  of the day: for example you may want to rebuild the XML Sitemap of your site
+  at 2:00AM in the morning, where usually only a few people are visiting your
+  site. You can even move some tasks to be executed only once a month (log
   rotation, old records expiry...).
 
-- If you have tasks that should be executed very often, but don't want to 
-  execute ALL drupal cron tasks that often! For example, you may want to check 
-  for emails that needs to be sent to your users every 2 minutes. Standard cron 
-  is managed in a "monolithic" way, so even if you find out how to execute it 
+- If you have tasks that should be executed very often, but don't want to
+  execute ALL drupal cron tasks that often! For example, you may want to check
+  for emails that needs to be sent to your users every 2 minutes. Standard cron
+  is managed in a "monolithic" way, so even if you find out how to execute it
   every 2 minutes, you will end in having all cron tasks executed so often, with
   a lot of performance problems.
 
-- Fine tune cron cache management : drupal cron will invalidate variable cache 
-  every cron run, and this is a great performance problem if you have a 
-  frequently called task. Elysia cron optimize cache management, and doesn't 
+- Fine tune cron cache management : drupal cron will invalidate variable cache
+  every cron run, and this is a great performance problem if you have a
+  frequently called task. Elysia cron optimize cache management, and doesn't
   need to invalidate cache.
 
-- Setup tasks that should be run at a precise time: for example if you want to 
+- Setup tasks that should be run at a precise time: for example if you want to
   send a SimpleNews newsletter every monday at 9:00AM, you can do it.
 
-- Parallel execution: if you have a task that takes a lot of time to execute, 
-  you can setup a different channel for it so it won't block other tasks that 
+- Parallel execution: if you have a task that takes a lot of time to execute,
+  you can setup a different channel for it so it won't block other tasks that
   need to be executed every 5 minutes.
 
 - Turn off (disable) a cron task/feature you don't need.
 
 - Debug system cron problems. If your cron does not terminate correctly you can
-  use extended logging, see at each cron timings and disable task to track down 
+  use extended logging, see at each cron timings and disable task to track down
   the problem.
 
 -----------------------------------------------------------------------------
@@ -93,11 +93,11 @@ Channels are groups of tasks. Each channel is a "parallel line" of execution
 Tasks inside a channel will be executed sequentially (if they should).
 
 WARNING: It's not recommended to create more than 2 or 3 channels.
-Every channel will increase the delay between each cron check (of the same 
+Every channel will increase the delay between each cron check (of the same
 channel), because each cron call will cycle between all channels.
 So, for example:
 If you have 1 channel it will be checked once a minute.
-If you have 2 channel each one will be checked every 2 minutes (almost usually, 
+If you have 2 channel each one will be checked every 2 minutes (almost usually,
 when the other one is running it will be checked once a minute).
 It you have 10 channels there will be a check every 10 minutes... if you have
 a job that should be executed every 5 minutes it won't do so!
@@ -106,10 +106,10 @@ a job that should be executed every 5 minutes it won't do so!
 EXPORT VIA CTOOLS/FEATURES MODULE
 -----------------------------------------------------------------------------
 
-With 2.0 version of Elysia Cron you can use "Bulk Export" functionality of 
+With 2.0 version of Elysia Cron you can use "Bulk Export" functionality of
 "Chaos Tools Suite" to export cron settings.
 To use it simply enable all modules, go to Structure > Bulk Exporter and
-select the tasks you want to export settings. You can also select all 
+select the tasks you want to export settings. You can also select all
 "elysia_cron" prefixed variables to export global options.
 Than generate the module.
 
@@ -117,7 +117,7 @@ The generated code will set the new defaults of elysia cron settings. This way
 you can simply enable it to use them, but you are free to override them in
 the future using the normal settings page.
 Note that if you want to delete all overrides, and return to exported settings,
-you should do a "reset to defaults" from elysia cron settings page. 
+you should do a "reset to defaults" from elysia cron settings page.
 
 You can also use "Features" module to create a Feature module will the settings
 you need.
@@ -133,7 +133,32 @@ DRUSH SUPPORT
 
 Elysia Cron 2.0 adds a simple support for Drush module.
 
-You can execute the "elysia-cron" command to run cron.
+Run all cron tasks in all active modules for specified site using elysia cron
+system. This replaces the standard "core-cron" drush handler.
+
+Examples:
+ elysia-cron run                           Run all cron tasks in all active
+                                           modules (as the standard "core-cron")
+ elysia-cron run --verbose                 Run all cron tasks in verbose mode
+ elysia-cron run @channel                  Run all cron tasks in specified
+                                           channel
+ elysia-cron run search_cron --ignore-time Run only search index
+                                           build task (force execution)
+ elysia-cron list --elysia-cron-verbose    List all channels/tasks
+                                           in verbose mode
+ elysia-cron disable search_cron           Disable search index build task
+
+Options:
+ --elysia-cron-verbose                     enable extended output (the same as
+                                           --verbose, but without enabling drush
+                                           verbose mode)
+ --ignore-disable                          run channels/tasks even if disabled
+ --ignore-running                          run channels/tasks even
+                                           if already running
+ --ignore-time                             run channels/tasks even if not ready
+                                           for execution
+ --quiet                                   suppress all output
+ --verbose                                 enable extended output
 
 -----------------------------------------------------------------------------
 RULES AND SCRIPT SYNTAX
@@ -150,17 +175,17 @@ RULES AND SCRIPT SYNTAX
  |  |  |  |  |
  *  *  *  *  *
 
-Each of the patterns from the first five fields may be either * (an asterisk), 
-which matches all legal values, or a list of elements separated by commas 
+Each of the patterns from the first five fields may be either * (an asterisk),
+which matches all legal values, or a list of elements separated by commas
 (see below).
 
-For "day of the week" (field 5), 0 is considered Sunday, 6 is Saturday (7 is 
+For "day of the week" (field 5), 0 is considered Sunday, 6 is Saturday (7 is
 an illegal value)
 
-A job is executed when the time/date specification fields all match the current 
-time and date. There is one exception: if both "day of month" and "day of week" 
-are restricted (not "*"), then either the "day of month" field (3) or the "day 
-of week" field (5) must match the current day (even though the other of the two 
+A job is executed when the time/date specification fields all match the current
+time and date. There is one exception: if both "day of month" and "day of week"
+are restricted (not "*"), then either the "day of month" field (3) or the "day
+of week" field (5) must match the current day (even though the other of the two
 fields need not match the current day).
 
 2. FIELDS OPERATOR
@@ -169,13 +194,13 @@ fields need not match the current day).
 There are several ways of specifying multiple date/time values in a field:
 
 * The comma (',') operator specifies a list of values, for example: "1,3,4,7,8"
-* The dash ('-') operator specifies a range of values, for example: "1-6", which 
+* The dash ('-') operator specifies a range of values, for example: "1-6", which
   is equivalent to "1,2,3,4,5,6"
-* The asterisk ('*') operator specifies all possible values for a field. For 
-  example, an asterisk in the hour time field would be equivalent to 'every hour' 
+* The asterisk ('*') operator specifies all possible values for a field. For
+  example, an asterisk in the hour time field would be equivalent to 'every hour'
   (subject to matching other specified fields).
-* The slash ('/') operator (called "step") can be used to skip a given number of 
-  values. For example, "*/3" in the hour time field is equivalent to 
+* The slash ('/') operator (called "step") can be used to skip a given number of
+  values. For example, "*/3" in the hour time field is equivalent to
   "0,3,6,9,12,15,18,21".
 
 3. EXAMPLES
@@ -190,10 +215,10 @@ There are several ways of specifying multiple date/time values in a field:
 4. SCRIPTS
 ---------------------------------
 
-You can use the script section to easily create new jobs (by calling a php 
+You can use the script section to easily create new jobs (by calling a php
 function) or to change the scheduling of an existing job.
 
-Every line of the script can be a comment (if it starts with #) or a job 
+Every line of the script can be a comment (if it starts with #) or a job
 definition.
 
 The syntax of a job definition is:
@@ -207,10 +232,10 @@ The syntax of a job definition is:
 * [job]: could be the name of a supported job (for example: 'search_cron') or a
   function call, ending with ; (for example: 'process_queue();').
 
-A comment on the line just preceding a job definition is considered the job 
+A comment on the line just preceding a job definition is considered the job
 description.
 
-Remember that script OVERRIDES all settings on single jobs sections or channel 
+Remember that script OVERRIDES all settings on single jobs sections or channel
 sections of the configuration
 
 5. EXAMPLE OF SCRIPT
@@ -219,7 +244,7 @@ sections of the configuration
 # Search indexing every 2 hours (i'm setting this as the job description)
 0 */2 * * * search_cron
 
-# I'll check for module status only on sunday nights 
+# I'll check for module status only on sunday nights
 # (and this is will not be the job description, see the empty line below)
 
 0 2 * * 0 update_status_cron

+ 7 - 7
sites/all/modules/contrib/dev/elysia_cron/cron.php

@@ -8,9 +8,11 @@
 if (!file_exists('includes/bootstrap.inc')) {
   if (!empty($_SERVER['DOCUMENT_ROOT']) && file_exists($_SERVER['DOCUMENT_ROOT'] . '/includes/bootstrap.inc')) {
     chdir($_SERVER['DOCUMENT_ROOT']);
-  } elseif (preg_match('@^(.*)[\\\\/]sites[\\\\/][^\\\\/]+[\\\\/]modules[\\\\/]([^\\\\/]+[\\\\/])?elysia(_cron)?$@', getcwd(), $r) && file_exists($r[1] . '/includes/bootstrap.inc')) {
+  }
+  elseif (preg_match('@^(.*)[\\\\/]sites[\\\\/][^\\\\/]+[\\\\/]modules[\\\\/]([^\\\\/]+[\\\\/])?elysia(_cron)?$@', getcwd(), $r) && file_exists($r[1] . '/includes/bootstrap.inc')) {
     chdir($r[1]);
-  } else {
+  }
+  else {
     die("Cron Fatal Error: Can't locate bootstrap.inc. Check cron.php position.");
   }
 }
@@ -21,16 +23,14 @@ if (!file_exists('includes/bootstrap.inc')) {
 define('DRUPAL_ROOT', getcwd());
 
 include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
-drupal_override_server_variables(array(
-  'SCRIPT_NAME' => '/cron.php',
-));
+drupal_override_server_variables(array('SCRIPT_NAME' => '/cron.php'));
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
-if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
+if ((variable_get('cron_key') && empty($_GET['cron_key'])) || !empty($_GET['cron_key']) && variable_get('cron_key') != $_GET['cron_key']) {
   watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
   drupal_access_denied();
 }
-elseif (variable_get('maintenance_mode', 0)) {
+elseif (variable_get('maintenance_mode', 0) && !variable_get('elysia_cron_run_maintenance', FALSE)) {
   watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
   drupal_access_denied();
 }

+ 0 - 13
sites/all/modules/contrib/dev/elysia_cron/elysia_cron-multilingual-image-path-2096571-1.patch

@@ -1,13 +0,0 @@
-diff --git a/elysia_cron.admin.inc b/elysia_cron.admin.inc
-index fa5c6de..4011d91 100644
---- a/elysia_cron.admin.inc
-+++ b/elysia_cron.admin.inc
-@@ -42,7 +42,7 @@ function elysia_cron_admin_page() {
- 
-   $rows = array();
-   
--  $ipath = url(drupal_get_path('module', 'elysia_cron') . '/images/icon_');
-+  $ipath = file_create_url(drupal_get_path('module', 'elysia_cron') . '/images/icon_');
-   
-   foreach ($elysia_cron_settings_by_channel as $channel => $data) {
-     $running = elysia_cron_is_channel_running($channel);

+ 322 - 196
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.admin.inc

@@ -1,35 +1,34 @@
 <?php
 
-/*******************************************************************************
- * INTERFACE
- *
- * WARN: Below this point the word "context" should be avoided (use channel)
- *   Disabled should always be referenced as "disabled" (in db is "disable" for 
- *   compatibility with Ctools )
- ******************************************************************************/
+/**
+ * @file
+ * Admin page callbacks for the elysia cron module.
+ */
 
+/**
+ * Page callback for 'admin/config/system/cron' path.
+ *
+ * @return array
+ *   Renderable array.
+ */
 function elysia_cron_admin_page() {
   $aoutput = array();
-  $aoutput[] = drupal_get_form('elysia_cron_run_form');
 
-  $output = '';
+  if (elysia_cron_access('execute elysia_cron')) {
+    $aoutput[] = drupal_get_form('elysia_cron_run_form');
+  }
 
+  $output = '';
   elysia_cron_initialize();
+  global $_elysia_cron_settings_by_channel;
 
-  global $elysia_cron_settings, $elysia_cron_settings_by_channel, $elysia_cron_current_channel, $cron_completed, $cron_completed_time;
-
-  $v = variable_get('elysia_cron_disabled', false);
-  $output .= '<p>' . t('Global disable') . ': <i>' . ($v ? '<span class="warn">' . t('YES') . '</span>' : 'no') . '</i></p>';
-  $output .= '<p>' . t('Last channel executed') . ': <i>' . (($c = elysia_cron_last_channel()) ? $c : t('n/a')) . '</i></p>';
-
-  if (EC_DRUPAL_VERSION < 7) {
-    if (_ec_variable_get('elysia_cron_semaphore', 0)) {
-      $output .= '<p><span class="warn">' . t('Global semaphore active since !date', array('!date' => elysia_cron_date(_ec_variable_get('elysia_cron_semaphore', 0)))) . '</span></p>';
-    }
-  }
+  $global_disabled = variable_get('elysia_cron_disabled', FALSE);
+  $last_channel = elysia_cron_last_channel();
+  $output .= '<p>' . t('Global disable') . ': <i>' . ($global_disabled ? '<span class="warn">' . t('YES') . '</span>' : t('no')) . '</i></p>';
+  $output .= '<p>' . t('Last channel executed') . ': <i>' . ($last_channel ? $last_channel : t('n/a')) . '</i></p>';
 
   $running = '';
-  foreach ($elysia_cron_settings_by_channel as $channel => $data) {
+  foreach ($_elysia_cron_settings_by_channel as $channel => $data) {
     if (elysia_cron_is_channel_running($channel)) {
       $running .= $channel . ' ';
     }
@@ -39,19 +38,33 @@ function elysia_cron_admin_page() {
   }
 
   $output .= '<p>' . t('Last run') . ': ' . elysia_cron_date(_ec_variable_get('elysia_cron_last_run', 0)) . '</p>';
-
   $rows = array();
-  
-  $ipath = file_create_url(drupal_get_path('module', 'elysia_cron') . '/images/icon_');
-  
-  foreach ($elysia_cron_settings_by_channel as $channel => $data) {
+  $ipath = drupal_get_path('module', 'elysia_cron') . '/images/icon_';
+
+  foreach ($_elysia_cron_settings_by_channel as $channel => $data) {
     $running = elysia_cron_is_channel_running($channel);
     $rows[] = array(
-      array('data' => '<h3>' . t('Channel') . ': ' . $channel . ($data['#data']['disabled'] ? ' <span class="warn">(' . t('DISABLED') . ')</span>' : '') . '</h3>', 'colspan' => 2, 'header' => 'true'),
-      array('data' => elysia_cron_date($data['#data']['last_run']), 'header' => 'true'),
-      array('data' => $data['#data']['last_execution_time'] . 's ' . t('(Shutdown: !shutdown)', array('!shutdown' => $data['#data']['last_shutdown_time'] . 's')), 'header' => 'true'),
-      array('data' => $data['#data']['execution_count'], 'header' => 'true'),
-      array('data' => $data['#data']['avg_execution_time'] . 's / ' . $data['#data']['max_execution_time'] . 's', 'header' => 'true'),
+      array(
+        'data' => '<h3>' . t('Channel') . ': ' . $channel . ($data['#data']['disabled'] ? ' <span class="warn">(' . t('DISABLED') . ')</span>' : '') . '</h3>',
+        'colspan' => 2,
+        'header' => TRUE,
+      ),
+      array(
+        'data' => elysia_cron_date($data['#data']['last_run']),
+        'header' => TRUE,
+      ),
+      array(
+        'data' => $data['#data']['last_execution_time'] . 's ' . t('(Shutdown: !shutdown)', array('!shutdown' => $data['#data']['last_shutdown_time'] . 's')),
+        'header' => TRUE,
+      ),
+      array(
+        'data' => $data['#data']['execution_count'],
+        'header' => TRUE,
+      ),
+      array(
+        'data' => $data['#data']['avg_execution_time'] . 's / ' . $data['#data']['max_execution_time'] . 's',
+        'header' => TRUE,
+      ),
     );
     $messages = '';
     if ($running) {
@@ -68,10 +81,14 @@ function elysia_cron_admin_page() {
       $messages .= implode(', ', $msg) . '<br />';
     }
     if ($messages) {
-      $rows[] = array( '', '', array('data' => $messages, 'colspan' => 4, 'header' => true) );
-      $rows[] = array( array('data' => '', 'colspan' => 6) );
+      $rows[] = array(
+        '',
+        '',
+        array('data' => $messages, 'colspan' => 4, 'header' => TRUE),
+      );
+      $rows[] = array(array('data' => '', 'colspan' => 6));
     }
-    
+
     foreach ($data as $job => $conf) {
       $icon = 'idle';
       $caption = '<b>' . $job . '</b>';
@@ -90,16 +107,25 @@ function elysia_cron_admin_page() {
         $icon = 'waiting';
         $tip = t('Waiting for execution');
       }
-      
+
       if ($job != '#data') {
+        $force_run = elysia_cron_access('execute elysia_cron')
+          ? l(t('Force run'), 'admin/config/system/cron/execute/' . $job, array('attributes' => array('onclick' => 'return confirm("' . t('Force execution of !job?', array('!job' => $job)) . '");')))
+          : '';
+
+        $icon_image = theme('image', array(
+          'path' => $ipath . $icon . '.png',
+          'alt' => $tip,
+          'title' => $tip,
+        ));
         $rows[] = array(
-          array( 'data' => '<img src="' . $ipath . $icon . '.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" />', 'align' => 'right' ),
-          array( 'data' => $caption . ': <i>' . elysia_cron_description($job) . '</i> ', 'colspan' => 4 ),
-          array( 'data' => _dco_l(t('Force run'), _dcf_internal_path('admin/config/system/cron/execute/') . $job, array('attributes' => array('onclick' => 'return confirm("' . t('Force execution of !job?', array('!job' => $job)) . '");'))), 'align' => 'right'),
+          array('data' => $icon_image, 'align' => 'right'),
+          array('data' => $caption . ': <i>' . elysia_cron_description($job) . '</i> ', 'colspan' => 4),
+          array('data' => $force_run, 'align' => 'right'),
         );
         $rows[] = array(
           '',
-          $conf['rule'] . (!empty($conf['weight']) ? ' <small>(' . t('Weight') . ': ' . $conf['weight'] . ')</small>' : ''),
+          check_plain($conf['rule']) . (!empty($conf['weight']) ? ' <small>(' . t('Weight') . ': ' . $conf['weight'] . ')</small>' : ''),
           elysia_cron_date($conf['last_run']),
           $conf['last_execution_time'] . 's',
           $conf['execution_count'],
@@ -107,21 +133,38 @@ function elysia_cron_admin_page() {
         );
       }
     }
-    $rows[] = array('&nbsp;','','','','','');
+
+    $rows[] = array('&nbsp;', '', '', '', '', '');
   }
-  
-  $output .= _dco_theme('table', array('header' => array('', t('Job / Rule'), t('Last run'), t('Last exec time'), t('Exec count'), t('Avg/Max Exec time')), 'rows' => $rows));
+
+  $output .= theme('table', array(
+    'header' => array(
+      '',
+      t('Job / Rule'),
+      t('Last run'),
+      t('Last exec time'),
+      t('Exec count'),
+      t('Avg/Max Exec time'),
+    ),
+    'rows' => $rows,
+  ));
   $output .= '<br />';
-  
-  $output .= _dco_theme('table', array(
+
+  $legend_icons = array(
+    'idle' => theme('image', array('path' => $ipath . 'idle.png')),
+    'waiting' => theme('image', array('path' => $ipath . 'waiting.png')),
+    'running' => theme('image', array('path' => $ipath . 'running.png')),
+    'disabled' => theme('image', array('path' => $ipath . 'disabled.png')),
+  );
+  $output .= theme('table', array(
     'header' => array(t('Legend')),
     'rows' => array(
-      array('<img src="' . $ipath . 'idle.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job shouldn\'t do anything right now')),
-      array('<img src="' . $ipath . 'waiting.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is ready to be executed, and is waiting for system cron call')),
-      array('<img src="' . $ipath . 'running.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is running right now')),
-      array('<img src="' . $ipath . 'disabled.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is disabled by settings, and won\'t run until enabled again')),
-      array(t('Notes: job times don\'t include shutdown times (only shown on channel times).')),
-      array(t('If an abort occours usually the job is not properly terminated, and so job timings can be inaccurate or wrong.')),
+      array($legend_icons['idle'] . ' - ' . t("Job shouldn't do anything right now")),
+      array($legend_icons['waiting'] . ' - ' . t('Job is ready to be executed, and is waiting for system cron call')),
+      array($legend_icons['running'] . ' - ' . t('Job is running right now')),
+      array($legend_icons['disabled'] . ' - ' . t("Job is disabled by settings, and won't run until enabled again")),
+      array(t("Notes: job times don't include shutdown times (only shown on channel times).")),
+      array(t('If an abort occurs usually the job is not properly terminated, and so job timings can be inaccurate or wrong.')),
     ),
   ));
 
@@ -130,11 +173,17 @@ function elysia_cron_admin_page() {
     '#markup' => $output,
   );
 
-  return _dcr_render_array($aoutput);
+  return $aoutput;
 }
 
+/**
+ * Form builder for general settings form.
+ *
+ * @return array
+ *   From API array.
+ */
 function elysia_cron_settings_form() {
-  global $elysia_cron_settings, $elysia_cron_settings_by_channel;
+  global $_elysia_cron_settings_by_channel;
   elysia_cron_initialize();
 
   $form = array();
@@ -142,8 +191,8 @@ function elysia_cron_settings_form() {
   $form['prefix_1'] = array(
     '#type' => 'fieldset',
     '#title' => t('Click for help and cron rules and script syntax'),
-    '#collapsible' => true,
-    '#collapsed' => true,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
     '#description' => t(<<<EOT
 <h3>Fields order</h3>
 <pre>
@@ -190,7 +239,7 @@ or to change the scheduling of an existing job.</p>
 <code>
 &lt;-&gt; [rule] &lt;ch:CHANNEL&gt; [job]
 </code>
-<p>(Tokens betweens [] are mandatory)</p>
+<p>(Tokens between [] are mandatory)</p>
 <ul>
 <li>&lt;-&gt;: a line starting with "-" means that the job is DISABLED.</li>
 <li>[rule]: a crontab schedule rule. See above.</li>
@@ -216,13 +265,13 @@ or to change the scheduling of an existing job.</p>
 # Disable node_cron (i must set the cron rule even if disabled)
 - */15 * * * * node_cron
 
-# Launch function send_summary_mail('test@test.com', false); every night
+# Launch function send_summary_mail('test@test.com', FALSE); every night
 # And set its description to "Send daily summary"
 # Send daily summary
-0 1 * * *  send_summary_mail('test@test.com', false);
+0 1 * * *  send_summary_mail('test@test.com', FALSE);
 </pre>
 EOT
-),
+    ),
   );
 
   $form['prefix_2'] = array(
@@ -232,54 +281,71 @@ EOT
   $form['main'] = array(
     '#title' => t('Main'),
     '#type' => 'fieldset',
-    '#collapsible' => false,
-    '#collapsed' => false,
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE,
   );
   $form['main']['elysia_cron_disabled'] = array(
     '#title' => t('Global disable'),
     '#type' => 'checkbox',
-    '#default_value' => variable_get('elysia_cron_disabled', false),
+    '#default_value' => variable_get('elysia_cron_disabled', FALSE),
+  );
+  $form['main']['elysia_cron_run_maintenance'] = array(
+    '#title' => t('Run in maintenance'),
+    '#description' => t('Allow to run cron in maintenance mode.'),
+    '#type' => 'checkbox',
+    '#default_value' => variable_get('elysia_cron_run_maintenance', FALSE),
   );
 
   $form['installation'] = array(
     '#title' => t('Installation settings'),
     '#type' => 'fieldset',
-    '#collapsible' => true,
-    '#collapsed' => true,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
 
-  if (EC_DRUPAL_VERSION >= 7) {
-    $form['installation']['cron_safe_threshold'] = array(
-      '#type' => 'select',
-      '#title' => t('Run cron on visitor\'s requests, every'),
-      '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
-      '#description' => t('Setting a time here will enable the "poormanscron" method, which runs the Drupal cron operation using normal browser/page requests instead of having to set up a crontab to request the cron.php script. This approach requires that your site gets regular traffic/visitors in order to trigger the cron request.') . '<br />' .
-        t('This way is fine if you don\'t need a great control over job starting times and execution frequency.') . '<br />' .
-        t('If you need a fine grained control over cron timings use the crontab metod, as <a href="!cron_url">described in Drupal installation guide</a>.', array('!cron_url' => url('http://drupal.org/cron'))) . '<br />' .
-        t('If you have a very large site, or you need to execute some jobs very often (more than once an hour) refer to Elysia cron\'s INSTALL.TXT to improve main cron setup.'),
-      '#options' => array(0 => t('Never / Use external crontab')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
-    );
-  }
+  $options[0] = t('Never / Use external crontab');
+  $options += drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval');
+  $form['installation']['cron_safe_threshold'] = array(
+    '#type' => 'select',
+    '#title' => t("Run cron on visitor's requests, every"),
+    '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
+    '#description' => t('Setting a time here will enable the "poormanscron" method, which runs the Drupal cron operation using normal browser/page requests instead of having to set up a crontab to request the cron.php script. This approach requires that your site gets regular traffic/visitors in order to trigger the cron request.')
+    . '<br>'
+    . t("This way is fine if you don't need a great control over job starting times and execution frequency.")
+    . '<br />'
+    . t('If you need fine-grained control over cron timings use the crontab method, as <a href="!cron_url">described in Drupal installation guide</a>.', array('!cron_url' => url('http://drupal.org/cron')))
+    . '<br />'
+    . t("If you have a very large site, or you need to execute some jobs very often (more than once an hour) refer to Elysia cron's INSTALL.TXT to improve main cron setup."),
+    '#options' => $options,
+  );
+
+  $form['installation']['elysia_cron_queue_show_count'] = array(
+    '#title' => t('Show the number of items in queues'),
+    '#description' => t('Some queue backends may have performance issue related with counting items in queue. If you faced with it, just disable this option.'),
+    '#type' => 'checkbox',
+    '#default_value' => variable_get('elysia_cron_queue_show_count', TRUE),
+  );
 
   $form['installation']['cron_key'] = array(
     '#title' => t('Cron key'),
     '#type' => 'textfield',
-    '#default_value' => variable_get('cron_key', ''),
-    '#description' => t('This is used to avoid external cron calling. If you set this cron will by accessible only by calling http://site/cron.php?cron_key=XXX, so you\'ll need to modify system crontab to support this (Logged user with [administer elysia_cron] permission avoid this check).'),
+    '#default_value' => variable_get('cron_key'),
+    '#description' => t("This is used to avoid external cron calling. If you set this cron will by accessible only by calling <em>http://site/cron.php?cron_key=XXX</em>, so you'll need to modify system crontab to support this (Logged users with <em>execute elysia_cron</em> permission avoid this check).
+    <br>If you left this field empty, you can run cron without cron_key parameter, like this <em>http://site/cron.php</em>, but it <b>HIGHLY NOT RECOMMENDED</b>."),
   );
 
   $form['installation']['elysia_cron_allowed_hosts'] = array(
     '#title' => t('Allowed hosts'),
     '#type' => 'textfield',
     '#default_value' => variable_get('elysia_cron_allowed_hosts', ''),
-    '#description' => t('Insert a list of ip addresses separated by , that can run cron.php (Logged user with [administer elysia_cron] permission avoid this check).'),
+    '#description' => t('Insert a list of ip addresses separated by , that can run cron.php (Logged user with [execute elysia_cron] permission avoid this check).'),
   );
 
   $form['installation']['elysia_cron_default_rule'] = array(
     '#title' => t('Default schedule rule'),
     '#type' => 'textfield',
-    '#default_value' => variable_get('elysia_cron_default_rule', false),
-    '#description' => t('If you don\'t specify a rule for a process, and if it has not a module specified one, this rule will apply'),
+    '#default_value' => variable_get('elysia_cron_default_rule', FALSE),
+    '#description' => t("If you don't specify a rule for a process, and if it has not a module specified one, this rule will apply"),
   );
 
   if (!ini_get('safe_mode')) {
@@ -309,25 +375,25 @@ EOT
     '#description' => t('Enable extended logging (in watchdog)'),
   );
 
-  $default_ruless = '';
-  $default_rules = variable_get('elysia_cron_default_rules', $GLOBALS['elysia_cron_default_rules']);
+  $default_rules_human = '';
+  $default_rules = variable_get('elysia_cron_default_rules', _elysia_cron_default_rules());
   foreach ($default_rules as $dk => $dr) {
-    $default_ruless .= $dr . ' = ' . $dk . "\n";
+    $default_rules_human .= $dr . ' = ' . $dk . PHP_EOL;
   }
 
   $form['installation']['elysia_cron_default_rules'] = array(
     '#title' => t('Predefined rules'),
     '#type' => 'textarea',
     '#rows' => 5,
-    '#default_value' => $default_ruless,
+    '#default_value' => $default_rules_human,
     '#description' => t('You can put here standard rules used in your system, each one with its own caption. Put each rule in a separate line, in the form "caption = rule". For example: <i>"every 15 minutes = */15 * * * *"</i>.'),
   );
 
   $form['installation']['elysia_cron_alert_fieldset'] = array(
     '#title' => t('External cron tracking'),
     '#type' => 'fieldset',
-    '#collapsible' => true,
-    '#collapsed' => true,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
     '#description' => t('This lets you use an external tracking system like <a href="http://www.host-tracker.com/">Host Tracker</a> to be used to monitor the health of cron on your site. Point the tracking service to <a href="!cron-ping-url">!cron-ping-url</a>. If Elysia cron has been called within the time interval specified below, the ping page will return HTTP 200.  If not, the ping page will throw a 404 (page not found).', array('!cron-ping-url' => url('admin/build/cron/ping'))),
   );
   $form['installation']['elysia_cron_alert_fieldset']['elysia_cron_alert_interval'] = array(
@@ -341,49 +407,46 @@ EOT
   $form['elysia_cron_script_fieldset'] = array(
     '#title' => t('Script'),
     '#type' => 'fieldset',
-    '#collapsible' => true,
+    '#collapsible' => TRUE,
     '#collapsed' => !variable_get('elysia_cron_script', ''),
   );
   $form['elysia_cron_script_fieldset']['elysia_cron_script'] = array(
     '#type' => 'textarea',
     '#rows' => 20,
     '#default_value' => variable_get('elysia_cron_script', ''),
-    '#description' => t('You can specify new cron jobs or modify existing schedules by adding lines to the script.<br>' .
-      '<b>Warning</b> All rules specified in the script will OVERRIDE single job settings and channel settings (sections below).'),
+    '#description' => t('You can specify new cron jobs or modify existing schedules by adding lines to the script.')
+    . '<br />'
+    . t('<b>Warning</b> All rules specified in the script will OVERRIDE single job settings and channel settings (sections below).'),
   );
-  
+
   $form['single_job'] = array(
     '#title' => t('Single job settings'),
-    '#description' => 
-      '<b>'.t('Disabled').'</b>: '.t('Flag this to disable job execution').'<br />'.
-      '<b>'.t('Schedule rule').'</b>: '.t('Timing rule for the job. Leave empty to use default rule (shown after the field in parenthesis)').'<br />'.
-      '<b>'.t('Weight').'</b>: '.t('Use this to specify execution order: low weights are executed before high weights. Default value shown in parenthesis').'<br />'.
-      '<b>'.t('Channel').'</b>: '.t('Specify a channel for the job (create the channel if not exists)').'<br /><br />',
+    '#description' => '<b>' . t('Disabled') . '</b>: ' . t('Flag this to disable job execution') . '<br />'
+    . '<b>' . t('Schedule rule') . '</b>: ' . t('Timing rule for the job. Leave empty to use default rule (shown after the field in parenthesis)') . '<br />'
+    . '<b>' . t('Weight') . '</b>: ' . t('Use this to specify execution order: low weights are executed before high weights. Default value shown in parenthesis') . '<br />'
+    . '<b>' . t('Channel') . '</b>: ' . t('Specify a channel for the job (create the channel if not exists)') . '<br /><br />',
     '#type' => 'fieldset',
-    '#collapsible' => true,
-    //'#collapsed' => true,
+    '#collapsible' => TRUE,
   );
-  
+
   $jobchannels = array(
     '#title' => t('Job channel associations'),
     '#description' => t('Leave empty for default channel'),
     '#type' => 'fieldset',
-    '#collapsible' => true,
-    '#collapsed' => true,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
 
-  foreach ($elysia_cron_settings_by_channel as $channel => $cconf) {
+  foreach ($_elysia_cron_settings_by_channel as $channel => $cconf) {
     foreach ($cconf as $job => $conf) {
       if ($job != '#data' && empty($conf['expression'])) {
         $form['single_job']['elysia_cron_' . $job] = array(
-          '#title' => $job, // t('Job !job', array('!job' => $job)),
+          '#title' => $job,
           '#description' => elysia_cron_description($job),
           '#type' => 'fieldset',
-          '#collapsible' => true,
+          '#collapsible' => TRUE,
           '#collapsed' => !elysia_cron_get_job_rule($job) && !elysia_cron_get_job_weight($job) && !elysia_cron_is_job_disabled($job) && !elysia_cron_get_job_channel($job),
         );
-        //if (!$form['single_job']['elysia_cron_'.$job]['#collapsed'])
-        //  $form['single_job']['#collapsed'] = false;
 
         $rule = elysia_cron_get_job_rule($job);
         $options = array_merge(array('default' => t('Default') . ' (' . (!empty($default_rules[$conf['default_rule']]) ? $default_rules[$conf['default_rule']] : $conf['default_rule']) . ')'), $default_rules);
@@ -414,23 +477,18 @@ EOT
           '#description' => '(' . $conf['default_weight'] . ')',
         );
 
-        //$form['single_job']['elysia_cron_'.$job]['elysia_cron_'.$job.'_disabled'] = array(
         $form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_disabled_' . $job] = array(
           '#title' => t('Disabled'),
           '#type' => 'checkbox',
-          '#default_value' => elysia_cron_is_job_disabled($job, false),
+          '#default_value' => elysia_cron_is_job_disabled($job, FALSE),
         );
 
-        //$jobchannels['elysia_cron_'.$job.'_channel'] = array(
         $form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_channel_' . $job] = array(
-          '#title' => t('Channel'), // t('Channel for !job', array('!job' => $job)),
+          '#title' => t('Channel'),
           '#type' => 'textfield',
           '#size' => 20,
           '#default_value' => elysia_cron_get_job_channel($job),
         );
-
-        //if (elysia_cron_get_job_channel($job))
-        //  $jobchannels['#collapsed'] = false;
       }
     }
   }
@@ -438,13 +496,12 @@ EOT
   $form['channels'] = array(
     '#title' => t('Channels settings'),
     '#type' => 'fieldset',
-    '#collapsible' => true,
-    //'#collapsed' => $jobchannels['#collapsed'],
+    '#collapsible' => TRUE,
   );
 
-  foreach ($elysia_cron_settings_by_channel as $channel => $conf) {
+  foreach ($_elysia_cron_settings_by_channel as $channel => $conf) {
     $form['channels']['elysia_cron_ch_' . $channel] = array(
-      '#title' => $channel, // t('Channel !channel', array('!channel' => $channel)),
+      '#title' => $channel,
       '#type' => 'fieldset',
     );
     $form['channels']['elysia_cron_ch_' . $channel]['_elysia_cron_ch_disabled_' . $channel] = array(
@@ -458,12 +515,8 @@ EOT
       '#size' => 20,
       '#default_value' => elysia_cron_get_channel_rule($channel),
     );
-    //if (elysia_cron_is_channel_disabled($channel))
-    //  $form['channels']['#collapsed'] = false;
   }
 
-  //$form['channels']['jobchannels'] = $jobchannels;
-
   $form['buttons'] = array('#type' => 'actions');
   $form['buttons']['submit'] = array(
     '#type' => 'submit',
@@ -478,17 +531,22 @@ EOT
     elysia_cron_error('The settings have not been saved because of the errors.');
   }
 
-  return _dcr_form($form);
+  return $form;
 }
 
-function theme_elysia_cron_settings_form($_dco_variables) {
-  extract(_dcf_theme_form($_dco_variables));
+/**
+ * Theme function for general settings form.
+ *
+ * @param array $variables
+ *   Theme vars.
+ *
+ * @return string
+ *   Ready for print HTML.
+ */
+function theme_elysia_cron_settings_form(array &$variables) {
   $form = &$variables['form'];
 
   $output = '<script type="text/javascript"><!--' . "\n" .
-    /*'function _ec_select(editid, select) { if (select.value == \'custom\') {'.
-      '$ = jQuery; $(select).hide();$("#"+editid).show();$("#"+editid).focus();'.
-    '}}'.*/
     'function _ec_select(key, select) { if (select.value == \'custom\') {' .
       '$ = jQuery; $("#_ec_select_"+key).hide();$("#_ec_custom_"+key).show();$("#_ec_custom_"+key).focus();' .
     '}}' .
@@ -499,33 +557,28 @@ function theme_elysia_cron_settings_form($_dco_variables) {
   $i = 0;
   foreach (element_children($form['single_job']) as $c) {
     $key = substr($c, 12);
-    //print_r($form['single_job'][$c]);
     if ($i++ == 0) {
-      $coutput .= '<tr>' .
-        '<th>' . $form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]['#title'] . '</th>' .
-        '<th>' . $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#title'] . '</th>' .
-        '<th colspan="2">' . $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] . '</th>' .
-        '<th>' . $form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#title'] . '</th>' .
-      '</tr>';
+      $coutput .= '<tr>'
+        . '<th>' . $form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]['#title'] . '</th>'
+        . '<th>' . $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#title'] . '</th>'
+        . '<th colspan="2">' . $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] . '</th>'
+        . '<th>' . $form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#title'] . '</th>'
+        . '</tr>';
     }
 
-    //$def_rule = $form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#description'];
     $def_weight = $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#description'];
 
     $posted_key = $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#name'];
-    $posted_val = !empty($_REQUEST[$posted_key]) ? $_REQUEST[$posted_key] : false;
+    $posted_val = !empty($_REQUEST[$posted_key]) ? $_REQUEST[$posted_key] : FALSE;
 
     $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#prefix'] = '<span id="_ec_custom_' . $key . '" style="' . ($posted_val != 'custom' ? 'display: none;' : '') . '">';
     $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#suffix'] = '</span>';
     $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#title'] = NULL;
     $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#description'] = NULL;
-    //$form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#attributes']['style'] = ($posted_val != 'custom' ? 'display: none;' : '').'width: 20em; margin: 0';
     $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#prefix'] = '<span id="_ec_select_' . $key . '" style="' . ($posted_val == 'custom' ? 'display: none;' : '') . '">';
     $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#suffix'] = '</span>';
     $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#title'] = NULL;
     $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#description'] = NULL;
-    //$form['single_job'][$c]['_elysia_cron_seljob_rule_'.$key]['#attributes']['style'] = ($posted_val == 'custom' ? 'display: none;' : '').'width: 20em; margin: 0';
-    //$form['single_job'][$c]['_elysia_cron_seljob_rule_'.$key]['#attributes']['onchange'] = '_ec_select(\''.$form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#id'].'\', this)';
     $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#attributes']['onchange'] = '_ec_select(\'' . $key . '\', this)';
 
     $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] = NULL;
@@ -536,32 +589,30 @@ function theme_elysia_cron_settings_form($_dco_variables) {
     $form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#title'] = NULL;
     $form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#attributes']['style'] = 'margin: 0';
 
-    $coutput .= '<tr><td colspan="6"><b>' . $form['single_job'][$c]['#title'] . '</b>' . (($d = $form['single_job'][$c]['#description']) && $d != '-' ? ' <i>(' . $d . ')</i>' : '' ) . '</td></tr>';
-    $coutput .= '<tr>' .
-      '<td align="center">' . drupal_render($form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]) . '</td>' .
-      '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]) . drupal_render($form['single_job'][$c]['_elysia_cron_job_rule_' . $key]) . '</td>' .//'<td><small>'.$def_rule.'</small></td>'.
-      '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_weight_' . $key]) . '</td><td><small>' . $def_weight . '</small></td>' .
-      '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_channel_' . $key]) . '</td>' .
-    '</tr>';
+    $coutput .= '<tr><td colspan="6"><b>' . $form['single_job'][$c]['#title'] . '</b>' . (($d = $form['single_job'][$c]['#description']) && $d != '-' ? ' <i>(' . $d . ')</i>' : '') . '</td></tr>';
+    $coutput .= '<tr>'
+      . '<td align="center">' . drupal_render($form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]) . '</td>'
+      . '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]) . drupal_render($form['single_job'][$c]['_elysia_cron_job_rule_' . $key]) . '</td>'
+      . '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_weight_' . $key]) . '</td><td><small>' . $def_weight . '</small></td>'
+      . '<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_channel_' . $key]) . '</td>'
+      . '</tr>';
     drupal_render($form['single_job'][$c]);
   }
   $coutput .= '</table>';
 
   $form['single_job']['#children'] = $coutput;
-  //$form['single_job'][] = array('#type' => 'markup', '#markup' => $output);
 
   $coutput = '<table>';
 
   $i = 0;
-
   foreach (element_children($form['channels']) as $c) {
     $key = substr($c, 15);
     if ($i++ == 0) {
-      $coutput .= '<tr>' .
-        '<th>' . t('Name') . '</th>' .
-        '<th>' . $form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#title'] . '</th>' .
-        '<th>' . $form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#title'] . '</th>' .
-      '</tr>';
+      $coutput .= '<tr>'
+        . '<th>' . t('Name') . '</th>'
+        . '<th>' . $form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#title'] . '</th>'
+        . '<th>' . $form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#title'] . '</th>'
+        . '</tr>';
     }
 
     $form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#title'] = NULL;
@@ -569,11 +620,11 @@ function theme_elysia_cron_settings_form($_dco_variables) {
     $form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#title'] = NULL;
     $form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#attributes']['style'] = 'margin: 0';
 
-    $coutput .= '<tr>' .
-      '<td><b>' . $form['channels'][$c]['#title'] . '</b></td>' .
-      '<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]) . '</td>' .
-      '<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_rule_' . $key]) . '</td>' .
-    '</tr>';
+    $coutput .= '<tr>'
+      . '<td><b>' . $form['channels'][$c]['#title'] . '</b></td>'
+      . '<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]) . '</td>'
+      . '<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_rule_' . $key]) . '</td>'
+      . '</tr>';
     drupal_render($form['channels'][$c]);
   }
   $coutput .= '</table>';
@@ -581,33 +632,63 @@ function theme_elysia_cron_settings_form($_dco_variables) {
   $form['channels']['#children'] = $coutput;
 
   return $output . drupal_render_children($form);
-  //$form['channels'][] = array('#type' => 'markup', '#markup' => $output);
-  //return drupal_render(_dcr_form($form));
 }
 
-function elysia_cron_settings_form_validate($_dco_form, &$_dco_form_state) {
-  extract(_dcf_form_validate($_dco_form, $_dco_form_state));
-  global $elysia_cron_settings;
+/**
+ * Validate handler for 'elysia_cron_settings_form' form.
+ *
+ * @param array $form
+ *   Form API array.
+ * @param array $form_state
+ *   Filled form_state array with input values.
+ */
+function elysia_cron_settings_form_validate(array $form, array &$form_state) {
+  $values = $form_state['values'];
 
   $script = $form_state['values']['elysia_cron_script'];
   if ($script) {
-    $errors = elysia_cron_decode_script($script, false);
+    $errors = elysia_cron_decode_script($script, FALSE);
     if ($errors) {
       form_set_error('elysia_cron_script', t('Invalid lines:') . implode('<br>', $errors));
     }
   }
 
-  foreach ($form_state['values'] as $key => $value) {
-    if ($value && preg_match('/^_elysia_cron_([^_]+_[^_]+)_(.*)$/', $key, $r) && ($r[1] == 'job_rule' || $r[1] == 'ch_rule')) {
+  foreach ($values as $key => $value) {
+    if ($value && ((preg_match('/^_elysia_cron_([^_]+_[^_]+)_(.*)$/', $key, $r) && ($r[1] == 'job_rule' || $r[1] == 'ch_rule')) || $key == 'elysia_cron_default_rule')) {
       if (!preg_match('/^\\s*([0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+)\\s*$/', $value)) {
-        form_set_error($key, t('Invalid rule: !rule', array('!rule' => $value)));
+        form_set_error($key, t('Invalid rule: %rule', array('%rule' => $value)));
+      }
+    }
+  }
+
+  if (!empty($values['elysia_cron_default_rules'])) {
+    $rules = explode(PHP_EOL, $values['elysia_cron_default_rules']);
+    foreach ($rules as $rule) {
+      $rule = trim($rule);
+      if (empty($rule)) {
+        continue;
+      }
+
+      $rule = explode('=', $rule);
+      if (empty($rule[1])) {
+        form_set_error('elysia_cron_default_rules', t('Invalid rule: %rule', array('%rule' => $rule[0])));
+      }
+      elseif (!preg_match('/^\\s*([0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+)\\s*$/', trim($rule[1]))) {
+        form_set_error('elysia_cron_default_rules', t('Invalid rule: %rule', array('%rule' => $rule[0])));
       }
     }
   }
 }
 
-function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
-  extract(_dcf_form_validate($_dco_form, $_dco_form_state));
+/**
+ * Submit handler for 'elysia_cron_settings_form' form.
+ *
+ * @param array $form
+ *   Form API array.
+ * @param array $form_state
+ *   Filled form_state array with input values.
+ */
+function elysia_cron_settings_form_submit(array $form, array &$form_state) {
   $form_values = $form_state['values'];
 
   $op = isset($form_values['op']) ? $form_values['op'] : '';
@@ -616,7 +697,7 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
   unset($form_values['submit'], $form_values['reset'], $form_values['form_id'], $form_values['op'], $form_values['form_token']);
 
   $elysia_cron_default_rules = array();
-  $rules = explode("\n", $form_values['elysia_cron_default_rules']);
+  $rules = explode(PHP_EOL, $form_values['elysia_cron_default_rules']);
   foreach ($rules as $r) {
     if (trim($r)) {
       $rr = explode("=", $r);
@@ -641,24 +722,28 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
     else {
       $nullvalue = $r[1] != 'job_weight' ? !$value : !$value && $value !== '0';
 
-      //dprint($r[1].' '.$r[1].' '.$r[2]);
       if ($op == t('Reset to defaults') || $nullvalue) {
         switch ($r[1]) {
           case 'job_channel':
             elysia_cron_reset_job_channel($r[2]);
             break;
+
           case 'job_rule':
             elysia_cron_reset_job_rule($r[2]);
             break;
+
           case 'job_weight':
             elysia_cron_reset_job_weight($r[2]);
             break;
+
           case 'job_disabled':
             elysia_cron_reset_job_disabled($r[2]);
             break;
+
           case 'ch_disabled':
             elysia_cron_reset_channel_disabled($r[2]);
             break;
+
           case 'ch_rule':
             elysia_cron_reset_channel_rule($r[2]);
             break;
@@ -669,11 +754,13 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
           case 'job_channel':
             elysia_cron_set_job_channel($r[2], $value);
             break;
+
           case 'job_rule':
             if ($form_values['_elysia_cron_seljob_rule_' . $r[2]] == 'custom') {
               elysia_cron_set_job_rule($r[2], $value);
             }
             break;
+
           case 'seljob_rule':
             if ($value != 'custom') {
               if ($value == 'default') {
@@ -684,15 +771,19 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
               }
             }
             break;
+
           case 'job_weight':
             elysia_cron_set_job_weight($r[2], $value);
             break;
+
           case 'job_disabled':
             elysia_cron_set_job_disabled($r[2], $value);
             break;
+
           case 'ch_disabled':
             elysia_cron_set_channel_disabled($r[2], $value);
             break;
+
           case 'ch_rule':
             elysia_cron_set_channel_rule($r[2], $value);
             break;
@@ -701,6 +792,7 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
 
     }
   }
+
   if ($op == t('Reset to defaults')) {
     elysia_cron_message('The configuration options have been reset to their default values.');
   }
@@ -709,60 +801,89 @@ function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
   }
 }
 
+/**
+ * Build time in "ago" format.
+ *
+ * @param int $timestamp
+ *   Time of latest cron.
+ *
+ * @return string
+ *   Date in 'ago' format.
+ */
 function elysia_cron_date($timestamp) {
-  return $timestamp > 0 ? format_date($timestamp, EC_DRUPAL_VERSION >= 7 ? 'short' : 'small') : t('n/a');
-  //return date(variable_get('date_format_short', 'm/d/Y - H:i'), $timestamp);
+  return $timestamp > 0 ? t('!time ago', array('!time' => format_interval(REQUEST_TIME - $timestamp, 2))) : t('n/a');
 }
 
+/**
+ * Form builder for cron run form.
+ *
+ * @return array
+ *   From API array.
+ */
 function elysia_cron_run_form() {
-  $form = array();
   $form['runf'] = array(
     '#type' => 'fieldset',
+    '#access' => elysia_cron_access('execute elysia_cron'),
   );
   $form['runf']['run'] = array(
     '#type' => 'submit',
     '#value' => t('Run cron'),
   );
+
   return $form;
 }
 
-function elysia_cron_run_form_submit($_dco_form, &$_dco_form_state) {
+/**
+ * Submit handler for 'elysia_cron_run_form' form.
+ *
+ * @param array $form
+ *   Form API array.
+ * @param array $form_state
+ *   Filled form_state array with input values.
+ */
+function elysia_cron_run_form_submit(array $form, array &$form_state) {
   // Run cron manually from Cron form.
-  if (elysia_cron_run()) {
+  if (elysia_cron_run(TRUE)) {
     elysia_cron_message('Cron run successfully.');
   }
   else {
     elysia_cron_error('Cron run failed.');
   }
 
-  drupal_goto(_dcf_internal_path('admin/config/system/cron'));
+  drupal_goto('admin/config/system/cron');
 }
 
-function elysia_cron_execute_page($job = false) {
+/**
+ * Page callback for 'admin/config/system/cron/execute/%' path.
+ *
+ * @param string $job
+ *   Name of cron job to be executed.
+ */
+function elysia_cron_execute_page($job = '') {
   if (!$job) {
     elysia_cron_error('No job specified');
-    drupal_goto(_dcf_internal_path('admin/config/system/cron'));
+    drupal_goto('admin/config/system/cron');
   }
-  
-  elysia_cron_run_job($job, true, true, false); // Run also if disabled or not ready (but not if it's already running)
 
-  drupal_goto(_dcf_internal_path('admin/config/system/cron'));
-}
+  // Run also if disabled or not ready (but not if it's already running).
+  elysia_cron_run_job($job, TRUE, TRUE, FALSE);
 
-function elysia_cron_maintenance_page() {
-  $output = array();
-  $output[] = drupal_get_form('elysia_cron_reset_statistics_form');
-  
-  return _dcr_render_array($output);
+  drupal_goto('admin/config/system/cron');
 }
 
+/**
+ * Form builder for statistic reset form.
+ *
+ * @return array
+ *   From API array.
+ */
 function elysia_cron_reset_statistics_form() {
-  $form = array();
   $form['fieldset'] = array(
     '#type' => 'fieldset',
     '#title' => t('Reset statistics'),
-    '#description' => t('Deletes all cron execution statistics (Last run, last exec time, exec count, avg/max exec time...). Do not touch cron settings.<br /><b>This operation could not be reverted</b><br />'),
+    '#description' => t('Deletes all cron execution statistics (Last run, last exec time, exec count, avg/max exec time...). Do not touch cron settings.<br /><b>This operation can not be reverted</b><br />'),
   );
+
   $form['fieldset']['reset'] = array(
     '#type' => 'submit',
     '#value' => t('Reset'),
@@ -770,16 +891,21 @@ function elysia_cron_reset_statistics_form() {
       'onclick' => 'return confirm(\'' . htmlentities(t('Are you sure you want to reset statistics?')) . '\')',
     ),
   );
+
   return $form;
 }
 
-function elysia_cron_reset_statistics_form_submit($_dco_form, &$_dco_form_state) {
+/**
+ * Submit handler for 'elysia_cron_reset_statistics_form' form.
+ *
+ * @param array $form
+ *   Form API array.
+ * @param array $form_state
+ *   Filled form_state array with input values.
+ */
+function elysia_cron_reset_statistics_form_submit(array $form, array &$form_state) {
   elysia_cron_reset_stats();
-  
-  elysia_cron_message('Reset done.');
-  drupal_goto(_dcf_internal_path('admin/config/system/cron/maintenance'));
-}
 
-function elysia_cron_reset_page() {
-  elysia_cron_reset_statistics_form_submit(false, $res = array());
+  elysia_cron_message('Reset done.');
+  drupal_goto('admin/config/system/cron/maintenance');
 }

+ 134 - 0
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.api.php

@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Elysia cron module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * You can extend cron functionality in you modules by using elysia_cron api.
+ *
+ * With it you can:
+ * - have more than one cron job per module
+ * - have a different schedule rule for each cron job defined
+ * - set a description for each cron job.
+ *
+ * To do this you should add in you module a new hook. This is the syntax:
+ *
+ * - 'key' is the identifier for the task you are defining.
+ *  You can define a timing for the standard cron hook of the module by using
+ *  the "MODULENAME_cron" key. (See examples).
+ *
+ * - description:
+ *  a textual description of the job, used in elysia cron's status
+ *  page. Use the untranslated string, without the "t()" wrapper (elysia_cron
+ *  will apply it)
+ *
+ * - rule:
+ *  the crontab rule. For example: "0 * * * *" to execute the task every hour.
+ *
+ * - weight (optional):
+ *  a numerical value to define order of execution. (Default:0)
+ *
+ * - callback (optional):
+ *  you can define here a name of a PHP function that should
+ *  by called to execute the task. This is not mandatory: if you don't specify
+ *  it Elysia cron will search for a function called like the task KEY.
+ *  If this function is not found, Elysia cron will call the "hook_cronapi"
+ *  function with $op = 'execute' and $job = 'KEY' (the key of the task).
+ * (See examples)
+ *
+ * - arguments (optional):
+ *  an array of arguments passed to callback (only if callback is defined).
+ *
+ * - file/file path:
+ *  the PHP file that contains the callback (hook_menu's syntax).
+ *
+ * @param string $op
+ *   Operation: "list" or "execute".
+ * @param string|null $job
+ *   Name of current job or it is NULL if we define job list.
+ *
+ * @return array
+ *   Job list.
+ */
+function hook_cronapi($op, $job = NULL) {
+  // General example of all parameters.
+  $items['key'] = array(
+    'description' => 'string',
+    'rule' => 'string',
+    'weight' => 1234,
+    'callback' => 'function_name',
+    'arguments' => array('first', 'second', 3),
+    // External file, like in hook_menu.
+    'file' => 'string',
+    'file path' => 'string',
+  );
+
+  // Run function example_sendmail_cron() every 2 hours.
+  // Note: i don't need to define a callback, i'll use "example_sendmail_cron"
+  // function.
+  $items['example_sendmail_cron'] = array(
+    'description' => 'Send mail with news',
+    'rule' => '0 */2 * * *',
+  );
+
+  // Run example_news_fetch('all') every 5 minutes.
+  // Note: this function has argument.
+  $items['example_news_cron'] = array(
+    'description' => 'Send mail with news',
+    'rule' => '*/5 * * * *',
+    'callback' => 'example_news_fetch',
+    'arguments' => array('all'),
+  );
+
+  // Definition of rules list and embedded code.
+  if ($op == 'list') {
+    // Rules list.
+    $items['job1'] = array(
+      'description' => 'Send mail with news',
+      'rule' => '0 */2 * * *',
+    );
+
+    $items['job2'] = array(
+      'description' => 'Send mail with news',
+      'rule' => '*/5 * * * *',
+    );
+  }
+  elseif ($op == 'execute') {
+    // Embedded code.
+    switch ($job) {
+      case 'job1':
+        // ... job1 code.
+        break;
+
+      case 'job2':
+        // ... job2 code.
+        break;
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * Altering hook_cron definition.
+ *
+ * You can use the "hook_cron_alter" function to edit cronapi data of other
+ * modules.
+ *
+ * @param array $data
+ *   Array of cron rules.
+ */
+function hook_cron_alter(&$data) {
+  $data['key']['rule'] = '0 */6 * * *';
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */

+ 130 - 61
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.ctools.inc

@@ -1,11 +1,16 @@
 <?php
 
-/*******************************************************************************
- * EXPORTABLES
- ******************************************************************************/
+/**
+ * @file
+ * Ctools integration.
+ */
 
- // WARN Features button "Revert components" will reset also statistics 
- 
+/**
+ * Get default cron jobs.
+ *
+ * @return array
+ *   Cron jobs.
+ */
 function elysia_cron_get_ctools_defaults() {
   if (module_exists('ctools') && function_exists('ctools_include')) {
     ctools_include('export');
@@ -14,26 +19,34 @@ function elysia_cron_get_ctools_defaults() {
       return _ctools_export_get_defaults('elysia_cron', $export);
     }
   }
+
   return array();
 }
 
 /**
- * Ctools load callback
- * Ctools does not support override of PARTIAL record, this is an elysia cron specific replacement to support it
+ * Ctools load callback.
+ *
+ * Ctools does not support override of PARTIAL record,
+ * this is an elysia cron specific replacement to support it.
+ *
+ * @param string $name
+ *   Cron job name.
+ *
+ * @return object|null
+ *   Object to export or NULL if nothing found.
  */
 function elysia_cron_ctools_export_load($name) {
   $schema = ctools_export_get_schema('elysia_cron');
   if (!empty($schema)) {
     $export = $schema['export'];
-    
-    if (EC_DRUPAL_VERSION >= 7) {
-      $object = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron} where name = :name", array(':name' => $name))->fetch();
-    }
-    else {
-      $object = db_fetch_object(db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron} where name = '%s'", $name));
-    }
+
+    $object = db_select('elysia_cron', 'ec')
+      ->fields('ec', _elysia_cron_columns())
+      ->condition('name', $name)
+      ->execute()
+      ->fetch();
     $default_objects = _ctools_export_get_defaults('elysia_cron', $export);
-    
+
     if ($object) {
       if (isset($default_objects[$name])) {
         return _elysia_cron_ctools_export_load_object_db_and_code($object, $default_objects[$name], $export);
@@ -49,62 +62,84 @@ function elysia_cron_ctools_export_load($name) {
 }
 
 /**
- * Ctools load all callback
- * Ctools does not support override of PARTIAL record, this is an elysia cron specific replacement to support it
+ * Ctools load all callback.
+ *
+ * Ctools does not support override of PARTIAL record,
+ * this is an elysia cron specific replacement to support it.
+ *
+ * @return array
+ *   Array of object to export.
  */
 function elysia_cron_ctools_export_load_all() {
+  $result = array();
   $schema = ctools_export_get_schema('elysia_cron');
+
   if (empty($schema)) {
-    return array();
+    return $result;
   }
+
   $export = $schema['export'];
-    
-  if (EC_DRUPAL_VERSION >= 7) {
-    $objects = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron}")->fetchAll();
-  }
-  else {
-    $objects = array();
-    $rs = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron}");
-    while ($o = db_fetch_object($rs)) {
-      $objects[] = $o;
-    }
-  }
+
+  $objects = db_select('elysia_cron', 'ec')
+    ->fields('ec', _elysia_cron_columns())
+    ->execute()
+    ->fetchAll();
   $default_objects = _ctools_export_get_defaults('elysia_cron', $export);
-  
-  $result = array();
+
   foreach ($objects as $object) {
     $key = $object->{$export['key']};
     if (isset($default_objects[$key])) {
       $result[$key] = _elysia_cron_ctools_export_load_object_db_and_code($object, $default_objects[$key], $export);
       unset($default_objects[$key]);
-    } else {
+    }
+    else {
       $result[$key] = _elysia_cron_ctools_export_load_object_db($object, $export);
-    } 
+    }
   }
+
   foreach ($default_objects as $key => $object) {
     $result[$key] = _elysia_cron_ctools_export_load_object_code($object, $export);
   }
+
   return $result;
 }
 
-function _elysia_cron_ctools_export_load_object_db_and_code($object, $code_object, $export) {
-  $overridden = false;
+/**
+ * @param object $object
+ * @param object $code_object
+ * @param array $export
+ *
+ * @return object
+ */
+function _elysia_cron_ctools_export_load_object_db_and_code($object, $code_object, array $export) {
+  $overridden = FALSE;
+
   foreach ($code_object as $keyd => $value) {
     if (!isset($object->$keyd) || is_null($object->$keyd)) {
       $object->$keyd = $value;
     }
-    else if ($object->$keyd !== $value) {
-      $overridden = true;
+    else {
+      if ($object->$keyd !== $value) {
+        $overridden = TRUE;
+      }
     }
   }
+
   $object->table = 'elysia_cron';
   $object->export_type = EXPORT_IN_DATABASE | EXPORT_IN_CODE;
   if (!empty($export['export type string'])) {
     $object->{$export['export type string']} = $overridden ? t('Overridden') : t('Normal');
   }
+
   return $object;
 }
 
+/**
+ * @param object $object
+ * @param array $export
+ *
+ * @return object
+ */
 function _elysia_cron_ctools_export_load_object_db($object, $export) {
   $object->table = 'elysia_cron';
   $object->export_type = EXPORT_IN_DATABASE;
@@ -114,6 +149,12 @@ function _elysia_cron_ctools_export_load_object_db($object, $export) {
   return $object;
 }
 
+/**
+ * @param object $object
+ * @param array $export
+ *
+ * @return object
+ */
 function _elysia_cron_ctools_export_load_object_code($object, $export) {
   $object->table = 'elysia_cron';
   $object->export_type = EXPORT_IN_CODE;
@@ -125,12 +166,19 @@ function _elysia_cron_ctools_export_load_object_code($object, $export) {
 }
 
 /**
- * Ctools export object factory
- * Original ctools export factory (_ctools_export_unpack_object) does not handle NULL values correctly.
+ * Ctools export object factory.
+ *
+ * Original ctools export factory (_ctools_export_unpack_object)
+ * does not handle NULL values correctly.
  * This function does not support $schema['join'].
+ *
+ * @param array $schema
+ * @param object $data
+ *
+ * @return object
  */
-function elysia_cron_ctools_export_object_factory($schema, $data) {
-  $object = new stdClass;
+function elysia_cron_ctools_export_object_factory(array $schema, $data) {
+  $object = new stdClass();
 
   foreach ($schema['fields'] as $field => $info) {
     $object->$field = isset($data->$field) && !is_null($data->$field) ? (empty($info['serialize']) ? $data->$field : unserialize($data->$field)) : NULL;
@@ -140,21 +188,28 @@ function elysia_cron_ctools_export_object_factory($schema, $data) {
 }
 
 /**
- * Ctools export callback
- * Handles NULL value (it's not possible to do this with "field" export callback, because null values are rewritten before its call)
+ * Ctools export callback.
+ *
+ * Handles NULL value (it's not possible to do this with "field"
+ * export callback, because null values are rewritten before its call).
+ *
+ * @param object $object
+ * @param string $indent
+ *
+ * @return string
  */
 function elysia_cron_ctools_export_callback($object, $indent) {
   $table = 'elysia_cron';
   $schema = ctools_export_get_schema($table);
   $identifier = $schema['export']['identifier'];
 
-  $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . ";\n";
+  $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . ';' . PHP_EOL;
 
   if ($schema['export']['can disable']) {
-    $output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */'  . "\n";
+    $output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . PHP_EOL;
   }
   if (!empty($schema['export']['api']['current_version'])) {
-    $output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ";\n";
+    $output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ';' . PHP_EOL;
   }
 
   $fields = $schema['fields'];
@@ -167,36 +222,50 @@ function elysia_cron_ctools_export_callback($object, $indent) {
     if (!is_null($value) && $info['type'] == 'int') {
       $value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value;
     }
-    $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n";
+    $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ';' . PHP_EOL;
   }
 
-  return $output;  
+  return $output;
 }
 
 /**
- * Ctools export to hook code callback
- * Original "to hook code" callback doesn't support the replacement of "load/load all" callback (it simply ignores them, even if defined and supported elsewhere)
- * This code is equal to the original ctools one, but uses specific load callback 
+ * Ctools export to hook code callback.
+ *
+ * Original "to hook code" callback does not support the replacement
+ * of "load/load all" callback (it simply ignores them,
+ * even if defined and supported elsewhere)
+ * This code is equal to the original ctools one,
+ * but uses specific load callback.
+ *
+ * @param array $names
+ * @param string $name
+ *
+ * @return string
  */
-function elysia_cron_ctools_to_hook_code($names, $name) {
+function elysia_cron_ctools_to_hook_code(array $names, $name) {
+  $output = '';
+
   $table = 'elysia_cron';
   $schema = ctools_export_get_schema($table);
   $export = $schema['export'];
-  $output = '';
+
   $objects = elysia_cron_ctools_export_load_all();
   $objects = array_intersect_key($objects, array_flip($names));
+
   if ($objects) {
-    $output = "/**\n";
-    $output .= " * Implementation of hook_{$export['default hook']}()\n";
-    $output .= " */\n";
-    $output .= "function " . $name . "_{$export['default hook']}() {\n";
-    $output .= "  \${$export['identifier']}s = array();\n\n";
+    $output = '/**' . PHP_EOL;
+    $output .= " * Implementation of hook_{$export['default hook']}()" . PHP_EOL;
+    $output .= ' */' . PHP_EOL;
+    $output .= "function " . $name . "_{$export['default hook']}() {" . PHP_EOL;
+    $output .= "  \${$export['identifier']}s = array();" . PHP_EOL . PHP_EOL;
+
     foreach ($objects as $object) {
       $output .= ctools_export_crud_export($table, $object, '  ');
-      $output .= "  \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n";
+      $output .= "  \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};" . PHP_EOL . PHP_EOL;
     }
-    $output .= "  return \${$export['identifier']}s;\n";
-    $output .= "}\n";
+
+    $output .= "  return \${$export['identifier']}s;" . PHP_EOL;
+    $output .= '}' . PHP_EOL;
   }
 
   return $output;

+ 86 - 82
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.drush.inc

@@ -1,47 +1,51 @@
 <?php
 
-/*******************************************************************************
- * DRUSH SUPPORT
- ******************************************************************************/
+/**
+ * @file
+ * Drush integration for Elysia cron module.
+ */
 
+/**
+ * Detect, is it drush or not.
+ *
+ * @return bool
+ *   TRUE if code executed inside drush, FALSE otherwise.
+ */
 function elysia_cron_drush_detect() {
   return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))) && function_exists('drush_main');
 }
 
+/**
+ * Exit from drush execution.
+ */
 function elysia_cron_drush_die() {
-  if (function_exists('drush_bootstrap_finish')) {
-    // Only in Drush5
-    drush_bootstrap_finish();
-    drush_die();
-  } else {
-    // Drush4
-    drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE);
-    exit();
-  }
+  drush_set_context('DRUSH_EXECUTION_COMPLETED', TRUE);
+  drupal_exit();
 }
 
-function elysia_cron_drush_invoke($replace_core_cron = false) {
+/**
+ * Wrapper for drush_invoke().
+ */
+function elysia_cron_drush_invoke($replace_core_cron = FALSE) {
   $args = drush_get_arguments();
   array_shift($args);
-  
-  // If invoked like "core-cron" i do the same as that: execute "run"
+
+  // If invoked like 'core-cron' I do the same as that: execute 'run'.
   if ($replace_core_cron && empty($args)) {
-    $args = array("run");
+    $args = array('run');
   }
-  
+
   call_user_func_array('drush_elysia_cron_run_wrapper', $args);
   elysia_cron_drush_die();
 }
 
 /**
- * Implementation of hook_drush_command().
+ * Implements hook_drush_command().
  */
 function elysia_cron_drush_command() {
-  $items = array();
-
   $items['elysia-cron'] = array(
-    'description' => "Run all cron tasks in all active modules for specified site using elysia cron system. This replaces the standard \"core-cron\" drush handler.", // TODO dt
-    'callback'    => 'drush_elysia_cron_run_wrapper',
+    'description' => dt('Run all cron tasks in all active modules for specified site using elysia cron system. This replaces the standard "core-cron" drush handler.'),
+    'callback' => 'drush_elysia_cron_run_wrapper',
     'arguments' => array(
       'op' => 'Operation: list, run, disable, enable',
       'target' => 'Target of operation (optional): usually a task name or a channel name starting with "@"',
@@ -62,97 +66,97 @@ function elysia_cron_drush_command() {
       'ignore-time' => 'run channels/tasks even if not ready for execution',
       'ignore-running' => 'run channels/tasks even if already running',
     ),
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
   );
 
   return $items;
 }
 
 /**
- * A drush command callback.
- *
- * wraps the elysia_cron_run function, passing manual = true
+ * Custom callback for 'elysia-cron' drush command.
  */
-function drush_elysia_cron_run_wrapper($op = false, $target = false) {
-  /*
-    drush_log("test notice", "notice");
-    drush_log("test ok", "ok");
-    drush_log("test completed", "completed");
-    drush_log("test warning", "warning");
-    drush_log("test error", "error");
-    drush_print("print");
-  */
-  global $elysia_cron_drush;
-  
-  $quiet = drush_get_option("quiet", false);
-  $verbose = drush_get_option("verbose", false);
+function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) {
+  global $_elysia_cron_drush;
+
+  if (variable_get('maintenance_mode', FALSE)) {
+    if (!variable_get('elysia_cron_run_maintenance', FALSE)) {
+      drush_set_error('Cron run is not allowed in maintenance mode');
+      return;
+    }
+  }
+
+  $quiet = drush_get_option('quiet', FALSE);
+  $verbose = drush_get_option('verbose', FALSE);
   if (!$verbose) {
-    $verbose = drush_get_option("elysia-cron-verbose", false);
+    $verbose = drush_get_option('elysia-cron-verbose', FALSE);
   }
-  $elysia_cron_drush = $quiet ? 1 : !$verbose ? 2 : 3; 
-  
+  $_elysia_cron_drush = $quiet ? 1 : !$verbose ? 2 : 3;
+
   switch ($op) {
     case 'list':
-      global $elysia_cron_settings_by_channel;
+      global $_elysia_cron_settings_by_channel;
       elysia_cron_initialize();
-      foreach ($elysia_cron_settings_by_channel as $channel => $jobs) {
+
+      foreach ($_elysia_cron_settings_by_channel as $channel => $jobs) {
+        $lines = array();
         if (!$verbose) {
-          $line = array("@" . $channel);
-        } else {
-          $line = array("Channel: @" . $channel);
+          $line = array('@' . $channel);
+        }
+        else {
+          $line = array('Channel: @' . $channel);
           if ($running = elysia_cron_is_channel_running($channel)) {
-            $line[] = "RUNNING NOW, since " . elysia_cron_date($running);
+            $line[] = dt('Running, since !time', array('!time' => elysia_cron_date($running)));
           }
           if (!empty($jobs['#data']['disabled'])) {
-            $line[] = "DISABLED";
+            $line[] = dt('Disabled');
           }
           if (!$running) {
-            $line[] = "Last run: " . elysia_cron_date(_ec_variable_get('elysia_cron_last_run', 0));
+            $line[] = dt('Last run: !time', array('!time' => elysia_cron_date(_ec_variable_get('elysia_cron_last_run', 0))));
           }
         }
-        drush_print(implode($line, ", "));
-        foreach ($jobs as $job => $conf) if ($job{0} != '#') {
-          if (!$verbose) {
-            $line = array($job);
-          } else {
-            $line = array("- Job: " . $job);
-            if (!empty($conf['running'])) {
-              $line[] = "RUNNING NOW, since " . elysia_cron_date($conf['running']);
-            }
-            if (!empty($conf['disabled'])) {
-              $line[] = "DISABLED";
-            }
-            if (empty($conf['running']) && elysia_cron_should_run($conf)) {
-              $line[] = "Ready to run";
+        drush_print(implode($line, ', '));
+
+        foreach ($jobs as $job => $conf) {
+          $line = array();
+          if (strpos($job, '#') !== 0) {
+            if (!$verbose) {
+              drush_print($job);
             }
-            if (empty($conf['running'])) {
-              $line[] = "Last run: " . elysia_cron_date($conf['last_run']);
+            else {
+              $line['name'] = $job;
+              $line['status'] = empty($conf['disabled']) ? dt('Enabled') : dt('Disabled');
+              $line['run_status'] = empty($conf['running']) && elysia_cron_should_run($conf) ? dt('Ready to run') : dt('Waiting');
+              $line['run_status'] = !empty($conf['running']) ? dt('Running, since !time', array('!time' => elysia_cron_date($conf['running']))) : $line['run_status'];
+              $line['last_run'] = !empty($conf['last_run']) ? dt('Last run: !time', array('!time' => elysia_cron_date($conf['last_run']))) : '';
+              $lines[] = $line;
             }
           }
-          drush_print(implode($line, ", "));
+        }
+        if ($lines) {
+          drush_print_table($lines);
         }
       }
       break;
 
     case 'run':
       if (empty($target)) {
-        elysia_cron_run(true, drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
-        //drush_log("Cron run complete", "completed");
+        elysia_cron_run(FALSE, drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE));
       }
-      elseif ($target{0} == '@') {
+      elseif (strpos($target, '@') === 0) {
         elysia_cron_initialize();
         if (elysia_cron_channel_exists(substr($target, 1))) {
-          elysia_cron_run_channel(substr($target, 1), drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
-          //drush_log("Cron run complete", "completed");
-        } else {
+          elysia_cron_run_channel(substr($target, 1), drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE));
+        }
+        else {
           drush_set_error('Channel ' . substr($target, 1) . ' does not exists');
         }
       }
       else {
         elysia_cron_initialize();
         if (elysia_cron_job_exists($target)) {
-          elysia_cron_run_job($target, drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
-          //drush_log("Cron run complete", "completed");
-        } else {
+          elysia_cron_run_job($target, drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE));
+        }
+        else {
           drush_set_error('Job ' . $target . ' does not exists');
         }
       }
@@ -161,21 +165,21 @@ function drush_elysia_cron_run_wrapper($op = false, $target = false) {
     case 'disable':
     case 'enable':
       if (!empty($target)) {
-        if ($target{0} == '@') {
+        if (strpos($target, '@') === 0) {
           elysia_cron_set_channel_disabled(substr($target, 1), $op == 'disable');
-        } else {
+        }
+        else {
           elysia_cron_set_job_disabled($target, $op == 'disable');
         }
-        drush_log("Done", "ok");
-      } else {
+        drush_log('Done', 'ok');
+      }
+      else {
         drush_set_error('Target not specified');
       }
-      
       break;
 
-      break;
-      
     default:
       drush_print_help(drush_get_command());
+      break;
   }
 }

+ 5 - 5
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.info

@@ -1,12 +1,12 @@
 name = "Elysia Cron"
 description = "Extended cron support with crontab-like scheduling and other features."
 core = 7.x
-files[] = elysia_cron_update.php
-files[] = elysia_drupalconv.php
+
 configure = admin/config/system/cron
-; Information added by drupal.org packaging script on 2013-09-30
-version = "7.x-2.1+9-dev"
+
+; Information added by Drupal.org packaging script on 2016-10-10
+version = "7.x-2.3"
 core = "7.x"
 project = "elysia_cron"
-datestamp = "1380576625"
+datestamp = "1476088169"
 

+ 83 - 34
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.install

@@ -1,5 +1,13 @@
 <?php
 
+/**
+ * @file
+ * Install, update and uninstall functions for the elysia_cron module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
 function elysia_cron_schema() {
   $schema['elysia_cron'] = array(
     'fields' => array(
@@ -16,7 +24,7 @@ function elysia_cron_schema() {
       'rule' => array(
         'type' => 'varchar',
         'not null' => FALSE,
-        'length' => 32,
+        'length' => 256,
       ),
       'weight' => array(
         'type' => 'int',
@@ -54,7 +62,7 @@ function elysia_cron_schema() {
       ),
       'last_abort_function' => array(
         'type' => 'varchar',
-        'length' => 32,
+        'length' => 128,
         'no export' => TRUE,
       ),
       'last_execution_time' => array(
@@ -97,11 +105,9 @@ function elysia_cron_schema() {
       'object factory' => 'elysia_cron_ctools_export_object_factory',
       'load callback' => 'elysia_cron_ctools_export_load',
       'load all callback' => 'elysia_cron_ctools_export_load_all',
-      //'save callback' => 'elysia_cron_ctools_export_save',
       'export callback' => 'elysia_cron_ctools_export_callback',
-      //'import callback' => 'elysia_cron_ctools_import_callback',
       'to hook code callback' => 'elysia_cron_ctools_to_hook_code',
-      
+
       'default hook' => 'default_elysia_cron_rules',
       'api' => array(
         'owner' => 'elysia_cron',
@@ -111,50 +117,93 @@ function elysia_cron_schema() {
       ),
     ),
   );
+
   return $schema;
 }
 
 /**
- * Implementation of hook_install().
+ * Implements hook_install().
  */
 function elysia_cron_install() {
-  //drupal_install_schema('elysia_cron');
-  
-  // elysia_cron MUST be the first returned by module_list
-  // This is to ensure elysia_cron_cron is the first hook called by standard cron.php.
-  $min = db_query("select min(weight) from {system}")->fetchField();
-  
-  if ($min > -65535) {
-    $min = -65535; 
-  }
-  else {
-    $min--;
-  }
-  db_update('system')->fields(array('weight' => $min))->condition('name', 'elysia_cron')->execute();
-  
-  variable_set('elysia_cron_version', elysia_cron_version());
-  
-  drupal_set_message('Elysia cron installed. Setup could be found at ' . l(t('Settings page'), 'admin/config/system/cron') . '. See INSTALL.TXT for more info.');
+  // Elysia cron MUST be the first returned by module_list.
+  // This is to ensure elysia_cron_cron is the first hook
+  // called by standard cron.php.
+  $query = db_select('system');
+  $query->addExpression('MIN(weight)');
+  $min = $query->execute()->fetchField();
+
+  $min = ($min > -65535) ? -65535 : --$min;
+
+  db_update('system')
+    ->fields(array('weight' => $min))
+    ->condition('name', 'elysia_cron')
+    ->execute();
 }
 
 /**
- * Implementation of hook_uninstall().
+ * Implements hook_uninstall().
  */
 function elysia_cron_uninstall() {
-  $rs = db_query("select name from {variable} where name like 'elysia_cron_%%'");
-  foreach ($rs as $o) {
-    variable_del($o->name);
-  }
-
-  //drupal_uninstall_schema('elysia_cron');
+  $variables = db_select('variable', 'v')
+    ->fields('v', array('name'))
+    ->condition('v.name', 'elysia_cron_%', 'LIKE')
+    ->execute()
+    ->fetchCol();
 
-  drupal_set_message('Elysia cron uninstalled.');
+  foreach ($variables as $name) {
+    variable_del($name);
+  }
 }
 
-function elysia_cron_update_1() {
-  $cron_key = variable_get('elysia_cron_key', false);
+/**
+ * Use default cron_key variable.
+ */
+function elysia_cron_update_7201() {
+  $cron_key = variable_get('elysia_cron_key', FALSE);
   if ($cron_key) {
     variable_set('cron_key', $cron_key);
   }
+
   variable_del('elysia_cron_key');
-}
+}
+
+/**
+ * Increase elysia_cron last_abort_function size from 32 to 128 characters.
+ */
+function elysia_cron_update_7202() {
+  db_change_field('elysia_cron', 'last_abort_function', 'last_abort_function', array(
+    'type' => 'varchar',
+    'length' => 128,
+    'no export' => TRUE,
+  ));
+}
+
+/**
+ * Change length property of rule to 256 characters.
+ */
+function elysia_cron_update_7203() {
+  $spec = array(
+    'type' => 'varchar',
+    'not null' => FALSE,
+    'length' => 256,
+  );
+  db_change_field('elysia_cron', 'rule', 'rule', $spec);
+}
+
+/**
+ * Remove unused variables.
+ */
+function elysia_cron_update_7204() {
+  variable_del('elysia_cron_version');
+}
+
+/**
+ * Rename context variable to channel.
+ */
+function elysia_cron_update_7205() {
+  if ($last = variable_get('elysia_cron_last_context')) {
+    variable_set('elysia_cron_last_channel', $last);
+  }
+
+  variable_del('elysia_cron_last_context');
+}

File diff suppressed because it is too large
+ 548 - 391
sites/all/modules/contrib/dev/elysia_cron/elysia_cron.module


+ 297 - 89
sites/all/modules/contrib/dev/elysia_cron/elysia_cron_scheduler.inc

@@ -1,33 +1,46 @@
 <?php
 
-function elysia_cron_should_run($conf, $now = -1, $ignore_disable = false, $ignore_time = false) {
-  $prev_rule_run = 0; // What time SHOULD the job be executed last time
+/**
+ * @file
+ * Schedules cron runs.
+ */
+
+/**
+ * Function for cron run schedule.
+ */
+function elysia_cron_should_run($conf, $now = -1, $ignore_disable = FALSE, $ignore_time = FALSE) {
+  // What time SHOULD the job be executed last time.
+  $prev_rule_run = 0;
   if (!$ignore_disable && $conf['disabled']) {
-    return false;
+    return FALSE;
   }
   if ($ignore_time) {
-    return true;
+    return TRUE;
   }
   if ($now < 0) {
     $now = time();
   }
   if ((!$conf['last_run']) || ($now - $conf['last_run'] > 365 * 86400)) {
-    return true;
+    return TRUE;
   }
 
   $next_run = _elysia_cron_next_run($conf);
   return $now >= $next_run;
 }
 
+/**
+ * Helper function for cron run schedule.
+ */
 function _elysia_cron_next_run($conf) {
   if (!isset($conf['rule'])) {
-    return false;
+    return FALSE;
   }
-  
+
   $ranges = array(
     array(0, 59),
     array(0, 23),
-    array(1, 31), // TODO
+    // TODO.
+    array(1, 31),
     array(1, 12),
     array(0, 3000),
     array(0, 6),
@@ -35,20 +48,21 @@ function _elysia_cron_next_run($conf) {
 
   if (!preg_match('/^([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)$/', $conf['rule'], $rules)) {
     elysia_cron_warning('Invalid rule found: %rule', array('%rule' => $conf['rule']));
-    return false;
+    return FALSE;
   }
 
   $rule = array($rules[1], $rules[2], array($rules[3], $rules[5]), $rules[4]);
   $ruledec = array();
   $date = __cronDecodeDate($conf['last_run'], 1);
   $expected_date = __cronDecodeDate(!empty($conf['last_run_expected']) ? $conf['last_run_expected'] : 0);
-  
+
   for ($i = 0; $i < 4; $i++) {
     if ($i != 2) {
-      // Standard scheme for mins, hours, month
+      // Standard scheme for mins, hours, month.
       $ruledec[$i] = __cronDecodeRule($rule[$i], $ranges[$i][0], $ranges[$i][1]);
-    } else {
-      // For mday+week we follow another scheme
+    }
+    else {
+      // For mday+week we follow another scheme.
       $ruledec[$i] = __cronDecodeRuleMday($rule[2], $date[3], $date[4]);
     }
     $r = $ruledec[$i];
@@ -82,10 +96,13 @@ function _elysia_cron_next_run($conf) {
   return __cronEncodeDate($date);
 }
 
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronDecodeDate($timestamp, $min_diff = 0) {
   $time = floor($timestamp / 60);
   $time += $min_diff;
-  
+
   $date = $time ? getdate($time * 60) : 0;
   $date = array(
     $time ? $date['minutes'] : 0,
@@ -96,10 +113,17 @@ function __cronDecodeDate($timestamp, $min_diff = 0) {
   );
   return $date;
 }
+
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronEncodeDate($date) {
   return mktime($date[1], $date[0], 0, $date[3], $date[2], $date[4]);
 }
 
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronNextOrEqual($el, $arr, $range_start, $range_end) {
   if (empty($arr)) {
     return $el;
@@ -112,6 +136,9 @@ function __cronNextOrEqual($el, $arr, $range_start, $range_end) {
   return $range_end + reset($arr) + 1 - $range_start;
 }
 
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronDecodeRule($rule, $min, $max) {
   if ($rule == '*') {
     return array('n' => array(), 'd' => 0);
@@ -133,9 +160,12 @@ function __cronDecodeRule($rule, $min, $max) {
   return $result;
 }
 
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronDecodeRuleMday($rule, $month, $year) {
   $range_from = 1;
-  $range_to = $month != 2 ? (in_array($month, array(4,6,9,11)) ? 30 : 31) : ($year % 4 == 0 ? 29 : 28);
+  $range_to = $month != 2 ? (in_array($month, array(4, 6, 9, 11)) ? 30 : 31) : ($year % 4 == 0 ? 29 : 28);
   $r1 = __cronDecodeRule($rule[0], $range_from, $range_to);
   $r2 = __cronDecodeRule($rule[1], $range_from, $range_to);
   if ($r2['d']) {
@@ -147,18 +177,21 @@ function __cronDecodeRuleMday($rule, $month, $year) {
   }
   if ($r2['n']) {
     $r2['n'] = array_unique($r2['n']);
-    $r1['n'] = array_merge($r1['n'], __cronMonDaysFromWeekDays($year, $month, $r2['n']), __cronMonDaysFromWeekDays($year, $month + 1, $r2['n'], 31)); // Use always "31" and not $range_to, see http://drupal.org/node/1668302
+    // Use always "31" and not $range_to, see http://drupal.org/node/1668302.
+    $r1['n'] = array_merge($r1['n'], __cronMonDaysFromWeekDays($year, $month, $r2['n']), __cronMonDaysFromWeekDays($year, $month + 1, $r2['n'], 31));
   }
   return $r1;
 }
 
-// Used by elysia_cron_should_run
+/**
+ * Helper function for _elysia_cron_next_run().
+ */
 function __cronMonDaysFromWeekDays($year, $mon, $weekdays, $offset = 0) {
   if ($mon > 12) {
-    $year ++;
+    $year++;
     $mon = $mon - 12;
   }
-  
+
   $result = array();
   for ($i = 1; checkdate($mon, $i, $year); $i++) {
     $w = date('w', mktime(12, 00, 00, $mon, $i, $year));
@@ -173,76 +206,251 @@ function __cronMonDaysFromWeekDays($year, $mon, $weekdays, $offset = 0) {
  * TESTS
  ******************************************************************************/
 
+/**
+ * Test function for elysia_cron_should_run().
+ */
 function test_elysia_cron_should_run() {
   dprint("Start test");
-  $start = microtime(true);
-  
-  //mktime: hr min sec mon day yr
-  dprint(" 1." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 01, 0, 1, 2, 2008))));
-  dprint(" 2." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(15, 00, 0, 1, 2, 2008))));
-  dprint(" 3." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(11, 59, 0, 1, 3, 2008))));
-  dprint(" 4." . (true  == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 00, 0, 1, 3, 2008))));
-  dprint(" 5." . (false == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 3, 2008))));
-  dprint(" 6." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 3, 2008))));
-  dprint(" 7." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 4, 2008))));
-  dprint(" 8." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 2, 2008))));
-  dprint(" 9." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(0, 0, 0, 1, 3, 2008))));
-  dprint("10." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("11." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(0, 0, 0, 1, 6, 2008))));
-  dprint("12." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("13." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(00, 00, 0, 1, 7, 2008))));
-  dprint("14." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
-  dprint("15." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("16." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("17." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 6, 2008))));
-  dprint("18." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 28, 0, 1, 6, 2008))));
-  dprint("19." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 5, 2008))));
-  dprint("20." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 30, 0, 1, 5, 2008))));
-  dprint("21." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("22." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
-
-  dprint("23." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 28, 2008))));
-  dprint("24." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
-  dprint("25." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
-
-  dprint("26." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 1, 2009))));
-  dprint("27." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 7, 2009))));
-  dprint("28." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(23, 59, 0, 1, 7, 2009))));
-
-  dprint("29." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
-  dprint("30." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
-  dprint("31." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 3, 7, 2008))));
-  dprint("32." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 58, 0, 2, 6, 2009))));
-  dprint("33." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 2, 6, 2009))));
-  dprint("34." . (true  == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 58, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
-  dprint("35." . (false == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 11, 2008))));
-  dprint("36." . (true  == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 20, 2008))));
-  dprint("37." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("38." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("39." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("40." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 10, 2008))));
-  dprint("41." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
-  dprint("42." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 16, 2008))));
-
-  dprint("43." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("44." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("45." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 7, 2008))));
-  dprint("46." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 13, 2008))));
-  dprint("47." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 4, 2008)), mktime(23, 59, 0, 2, 5, 2008))));
-  dprint("48." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 5, 2008)), mktime(23, 59, 0, 2, 10, 2008))));
-  dprint("49." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 10, 2008)), mktime(23, 59, 0, 2, 17, 2008))));
-
-  dprint("49." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 58, 0, 2, 10, 2008)), mktime(8, 59, 0, 2, 10, 2008))));
-  dprint("50." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(9, 00, 0, 2, 10, 2008))));
-  dprint("51." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(17, 59, 0, 2, 10, 2008))));
-  dprint("52." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(18, 00, 0, 2, 10, 2008))));
-  dprint("53." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
-  dprint("54." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(19, 0, 0, 2, 10, 2008))));
-  dprint("55." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(9, 0, 0, 3, 10, 2008))));
-
-  dprint("56." . (true  == elysia_cron_should_run(array('rule' => '* * * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
-
-  dprint("End test (" . (microtime(true) - $start) . ")");
+  $start = microtime(TRUE);
+
+  // @mktime: hr min sec mon day yr.
+  dprint(" 1." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '0 12 * * *',
+    'last_run' => mktime(12, 0, 0, 1, 2, 2008),
+  ), mktime(12, 01, 0, 1, 2, 2008))));
+  dprint(" 2." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '0 12 * * *',
+    'last_run' => mktime(12, 0, 0, 1, 2, 2008),
+  ), mktime(15, 00, 0, 1, 2, 2008))));
+  dprint(" 3." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '0 12 * * *',
+    'last_run' => mktime(12, 0, 0, 1, 2, 2008),
+  ), mktime(11, 59, 0, 1, 3, 2008))));
+  dprint(" 4." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '0 12 * * *',
+    'last_run' => mktime(12, 0, 0, 1, 2, 2008),
+  ), mktime(12, 00, 0, 1, 3, 2008))));
+  dprint(" 5." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * *',
+    'last_run' => mktime(23, 59, 0, 1, 2, 2008),
+  ), mktime(0, 00, 0, 1, 3, 2008))));
+  dprint(" 6." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * *',
+    'last_run' => mktime(23, 59, 0, 1, 2, 2008),
+  ), mktime(23, 59, 0, 1, 3, 2008))));
+  dprint(" 7." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * *',
+    'last_run' => mktime(23, 59, 0, 1, 2, 2008),
+  ), mktime(0, 00, 0, 1, 4, 2008))));
+  dprint(" 8." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * *',
+    'last_run' => mktime(23, 58, 0, 1, 2, 2008),
+  ), mktime(23, 59, 0, 1, 2, 2008))));
+  dprint(" 9." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * *',
+    'last_run' => mktime(23, 58, 0, 1, 2, 2008),
+  ), mktime(0, 0, 0, 1, 3, 2008))));
+  dprint("10." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 5, 2008))));
+  dprint("11." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(0, 0, 0, 1, 6, 2008))));
+  dprint("12." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 6, 2008))));
+  dprint("13." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(00, 00, 0, 1, 7, 2008))));
+  dprint("14." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 29, 0, 1, 6, 2008))));
+  dprint("15." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 6, 2008))));
+  dprint("16." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 5, 2008))));
+  dprint("17." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 58, 0, 1, 6, 2008))));
+  dprint("18." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 58, 0, 1, 5, 2008),
+  ), mktime(23, 28, 0, 1, 6, 2008))));
+  dprint("19." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 28, 0, 1, 5, 2008),
+  ), mktime(23, 29, 0, 1, 5, 2008))));
+  dprint("20." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 28, 0, 1, 5, 2008),
+  ), mktime(23, 30, 0, 1, 5, 2008))));
+  dprint("21." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 28, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 5, 2008))));
+  dprint("22." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 0',
+    'last_run' => mktime(23, 28, 0, 1, 5, 2008),
+  ), mktime(23, 29, 0, 1, 6, 2008))));
+
+  dprint("23." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 5',
+    'last_run' => mktime(23, 59, 0, 2, 22, 2008),
+  ), mktime(23, 59, 0, 2, 28, 2008))));
+  dprint("24." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 5',
+    'last_run' => mktime(23, 59, 0, 2, 22, 2008),
+  ), mktime(23, 59, 0, 2, 29, 2008))));
+  dprint("25." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '29,59 23 * * 5',
+    'last_run' => mktime(23, 59, 0, 2, 22, 2008),
+  ), mktime(0, 0, 0, 3, 1, 2008))));
+
+  dprint("26." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 3',
+    'last_run' => mktime(23, 59, 0, 12, 31, 2008),
+  ), mktime(0, 0, 0, 1, 1, 2009))));
+  dprint("27." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 3',
+    'last_run' => mktime(23, 59, 0, 12, 31, 2008),
+  ), mktime(0, 0, 0, 1, 7, 2009))));
+  dprint("28." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * * 3',
+    'last_run' => mktime(23, 59, 0, 12, 31, 2008),
+  ), mktime(23, 59, 0, 1, 7, 2009))));
+
+  dprint("29." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * 2 5',
+    'last_run' => mktime(23, 59, 0, 2, 22, 2008),
+  ), mktime(23, 59, 0, 2, 29, 2008))));
+  dprint("30." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * 2 5',
+    'last_run' => mktime(23, 59, 0, 2, 22, 2008),
+  ), mktime(0, 0, 0, 3, 1, 2008))));
+  dprint("31." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * 2 5',
+    'last_run' => mktime(23, 59, 0, 2, 29, 2008),
+  ), mktime(23, 59, 0, 3, 7, 2008))));
+  dprint("32." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 * 2 5',
+    'last_run' => mktime(23, 59, 0, 2, 29, 2008),
+  ), mktime(23, 58, 0, 2, 6, 2009))));
+  dprint("33." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 * 2 5',
+    'last_run' => mktime(23, 59, 0, 2, 29, 2008),
+  ), mktime(23, 59, 0, 2, 6, 2009))));
+  dprint("34." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 *' . '/10 * *',
+    'last_run' => mktime(23, 58, 0, 1, 10, 2008),
+  ), mktime(23, 59, 0, 1, 10, 2008))));
+  dprint("35." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 *' . '/10 * *',
+    'last_run' => mktime(23, 59, 0, 1, 10, 2008),
+  ), mktime(23, 59, 0, 1, 11, 2008))));
+  dprint("36." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 *' . '/10 * *',
+    'last_run' => mktime(23, 59, 0, 1, 10, 2008),
+  ), mktime(23, 59, 0, 1, 20, 2008))));
+  dprint("37." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 4, 2008),
+  ), mktime(23, 59, 0, 1, 5, 2008))));
+  dprint("38." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 4, 2008),
+  ), mktime(23, 59, 0, 1, 6, 2008))));
+  dprint("39." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 6, 2008))));
+  dprint("40." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 5, 2008),
+  ), mktime(23, 58, 0, 1, 10, 2008))));
+  dprint("41." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 10, 2008))));
+  dprint("42." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5,10-15 * *',
+    'last_run' => mktime(23, 59, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 16, 2008))));
+
+  dprint("43." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 1, 4, 2008),
+  ), mktime(23, 59, 0, 1, 5, 2008))));
+  dprint("44." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 1, 5, 2008),
+  ), mktime(23, 59, 0, 1, 6, 2008))));
+  dprint("45." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 1, 6, 2008),
+  ), mktime(23, 59, 0, 1, 7, 2008))));
+  dprint("46." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 1, 6, 2008),
+  ), mktime(23, 59, 0, 1, 13, 2008))));
+  dprint("47." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 2, 4, 2008),
+  ), mktime(23, 59, 0, 2, 5, 2008))));
+  dprint("48." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 2, 5, 2008),
+  ), mktime(23, 59, 0, 2, 10, 2008))));
+  dprint("49." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '59 23 1-5 1 0',
+    'last_run' => mktime(23, 59, 0, 2, 10, 2008),
+  ), mktime(23, 59, 0, 2, 17, 2008))));
+
+  dprint("49." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(8, 58, 0, 2, 10, 2008),
+  ), mktime(8, 59, 0, 2, 10, 2008))));
+  dprint("50." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(8, 59, 0, 2, 10, 2008),
+  ), mktime(9, 00, 0, 2, 10, 2008))));
+  dprint("51." . (FALSE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(8, 59, 0, 2, 10, 2008),
+  ), mktime(17, 59, 0, 2, 10, 2008))));
+  dprint("52." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(8, 59, 0, 2, 10, 2008),
+  ), mktime(18, 00, 0, 2, 10, 2008))));
+  dprint("53." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(18, 00, 0, 2, 10, 2008),
+  ), mktime(18, 01, 0, 2, 10, 2008))));
+  dprint("54." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(18, 00, 0, 2, 10, 2008),
+  ), mktime(19, 0, 0, 2, 10, 2008))));
+  dprint("55." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *',
+    'last_run' => mktime(18, 00, 0, 2, 10, 2008),
+  ), mktime(9, 0, 0, 3, 10, 2008))));
+
+  dprint("56." . (TRUE == elysia_cron_should_run(array(
+    'rule' => '* * * * *',
+    'last_run' => mktime(18, 00, 0, 2, 10, 2008),
+  ), mktime(18, 01, 0, 2, 10, 2008))));
+
+  dprint("End test (" . (microtime(TRUE) - $start) . ")");
 }
-// Remove comment to run tests
-//test_elysia_cron_should_run();die();
+
+// Remove comment to run tests.
+// test_elysia_cron_should_run();die();

+ 0 - 230
sites/all/modules/contrib/dev/elysia_cron/elysia_cron_scheduler_old.inc

@@ -1,230 +0,0 @@
-<?php
-
-function elysia_cron_should_run($conf, $now = -1) {
-  if ($conf['disabled']) {
-    return false;
-  }
-
-  if ($now < 0) {
-    $now = time();
-  }
-
-  if ((!$conf['last_run']) || ($now - $conf['last_run'] > 365 * 86400)) {
-    return true;
-  }
-
-  if (!preg_match('/^([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)$/', $conf['rule'], $rules)) {
-    elysia_cron_warning('Invalid rule found: %rule', array('%rule' => $conf['rule']));
-    return false;
-  }
-
-  $weekdayspec = ($rules[5] != '*');
-  $mondayspec = ($rules[3] != '*');
-
-  $rules[5] = _cronDecodeRule($rules[5], 0, 6);
-  $rules[4] = _cronDecodeRule($rules[4], 1, 12);
-  $rules[3] = _cronDecodeRule($rules[3], 1, 31);
-  $rules[2] = _cronDecodeRule($rules[2], 0, 23);
-  $rules[1] = _cronDecodeRule($rules[1], 0, 59);
-
-  $lastT = _cronT($conf['last_run'] + 30);
-  $nowT = _cronT($now);
-  $nowTDelta = $nowT - $lastT + ($lastT > $nowT ? 12 * 31 * 24 * 60 : 0);
-  $year = date('Y', $conf['last_run']);
-
-  if ($mondayspec || (!$mondayspec && !$weekdayspec)) {
-    $first = -1;
-    foreach ($rules[4] as $mon) {
-      foreach ($rules[3] as $d) {
-        if (checkdate($mon, $d, $year)) {
-          foreach ($rules[2] as $h) {
-            foreach ($rules[1] as $m) {
-              $t = _cronT($mon, $d, $h, $m);
-              //dprint("* ".$t." L:".$lastT);
-              if ($first < 0) {
-                $first = $t;
-              }
-              if ($t > $lastT) {
-                $nextT = $t;
-                break 4;
-              }
-            }
-          }
-        }
-      }
-    }
-    //dprint("R: ".$nextT);
-    if (!$nextT) {
-      $nextT = $first;
-    }
-
-    $nextTDelta = $nextT - $lastT + ($lastT > $nextT ? 12 * 31 * 24 * 60 : 0);
-
-    //dprint($nowT.' '.$nowTDelta.' '.$nextT.' '.$nextTDelta);
-    if ($nowTDelta >= $nextTDelta) {
-      return true;
-    }
-  }
-  if ($weekdayspec) {
-    foreach ($rules[4] as $mon) {
-      foreach (_cronMonDaysFromWeekDays($year, $mon, $rules[5]) as $d) {
-        foreach ($rules[2] as $h) {
-          foreach ($rules[1] as $m) {
-            $t = _cronT($mon, $d, $h, $m);
-            //dprint("* ".$t." L:".$lastT);
-            if ($t > $lastT) {
-              $nextT = $t;
-              break 4;
-            }
-          }
-        }
-      }
-    }
-    //dprint("R: ".$nextT);
-    if (!$nextT) {
-      //Must get the first of following year (if day_of_week is specified it's not the same of previous one)
-      foreach ($rules[4] as $mon) {
-        foreach (_cronMonDaysFromWeekDays($year + 1, $mon, $rules[5]) as $d) {
-          foreach ($rules[2] as $h) {
-            foreach ($rules[1] as $m) {
-              $nextT = _cronT($mon, $d, $h, $m);
-              break 4;
-            }
-          }
-        }
-      }
-    }
-
-    $nextTDelta = $nextT - $lastT + ($lastT > $nextT ? 12 * 31 * 24 * 60 : 0);
-
-    //dprint($nowT.' '.$nowTDelta.' '.$nextT.' '.$nextTDelta);
-    if ($nowTDelta >= $nextTDelta) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-// Used by elysia_cron_should_run
-function _cronT($time, $d = -1, $h = -1, $m = -1) {
-  if ($d < 0) {
-    return date('n', $time) * 31 * 24 * 60 + date('j', $time) * 24 * 60 + date('H', $time) * 60 + date('i', $time);
-  }
-  else {
-    return $time * 31 * 24 * 60 + $d * 24 * 60 + $h * 60 + $m;
-  }
-}
-
-// Used by elysia_cron_should_run
-function _cronMonDaysFromWeekDays($year, $mon, $weekdays) {
-  $result = array();
-  for ($i = 1; checkdate($mon, $i, $year); $i++) {
-    $w = date('w', mktime(12, 00, 00, $mon, $i, $year));
-    if (in_array($w, $weekdays)) {
-      $result[] = $i;
-    }
-  }
-  return $result;
-}
-
-// Used by elysia_cron_should_run
-function _cronDecodeRule($rule, $min, $max) {
-  if ($rule == '*') {
-    return range($min, $max);
-  }
-
-  $result = array();
-  foreach (explode(',', $rule) as $token) {
-    if (preg_match('/^([0-9]+)-([0-9]+)$/', $token, $r)) {
-      $result = array_merge($result, range($r[1], $r[2]));
-    }
-    elseif (preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
-      for ($i = $min; $i <= $max; $i++) {
-        if ($i % $r[1] == 0) {
-          $result[] = $i;
-        }
-      }
-    }
-    elseif (is_numeric($token)) {
-      $result[] = $token;
-    }
-  }
-  return $result;
-}
-
-/*******************************************************************************
- * TESTS
- ******************************************************************************/
-
-function test_elysia_cron_should_run() {
-  dprint("Start test");
-  $start = microtime(true);
-  
-  //mktime: hr min sec mon day yr
-  dprint(" 1." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 01, 0, 1, 2, 2008))));
-  dprint(" 2." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(15, 00, 0, 1, 2, 2008))));
-  dprint(" 3." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(11, 59, 0, 1, 3, 2008))));
-  dprint(" 4." . (true  == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 00, 0, 1, 3, 2008))));
-  dprint(" 5." . (false == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 3, 2008))));
-  dprint(" 6." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 3, 2008))));
-  dprint(" 7." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 4, 2008))));
-  dprint(" 8." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 2, 2008))));
-  dprint(" 9." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(0, 0, 0, 1, 3, 2008))));
-  dprint("10." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("11." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(0, 0, 0, 1, 6, 2008))));
-  dprint("12." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("13." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(00, 00, 0, 1, 7, 2008))));
-  dprint("14." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
-  dprint("15." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("16." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("17." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 6, 2008))));
-  dprint("18." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 28, 0, 1, 6, 2008))));
-  dprint("19." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 5, 2008))));
-  dprint("20." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 30, 0, 1, 5, 2008))));
-  dprint("21." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("22." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
-
-  dprint("23." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 28, 2008))));
-  dprint("24." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
-  dprint("25." . (true  == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
-
-  dprint("26." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 1, 2009))));
-  dprint("27." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 7, 2009))));
-  dprint("28." . (true  == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(23, 59, 0, 1, 7, 2009))));
-
-  dprint("29." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
-  dprint("30." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
-  dprint("31." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 3, 7, 2008))));
-  dprint("32." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 58, 0, 2, 6, 2009))));
-  dprint("33." . (true  == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 2, 6, 2009))));
-  dprint("34." . (true  == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 58, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
-  dprint("35." . (false == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 11, 2008))));
-  dprint("36." . (true  == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 20, 2008))));
-  dprint("37." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("38." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("39." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("40." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 10, 2008))));
-  dprint("41." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
-  dprint("42." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 16, 2008))));
-
-  dprint("43." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
-  dprint("44." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
-  dprint("45." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 7, 2008))));
-  dprint("46." . (true  == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 13, 2008))));
-  dprint("47." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 4, 2008)), mktime(23, 59, 0, 2, 5, 2008))));
-  dprint("48." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 5, 2008)), mktime(23, 59, 0, 2, 10, 2008))));
-  dprint("49." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 10, 2008)), mktime(23, 59, 0, 2, 17, 2008))));
-
-  dprint("49." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 58, 0, 2, 10, 2008)), mktime(8, 59, 0, 2, 10, 2008))));
-  dprint("50." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(9, 00, 0, 2, 10, 2008))));
-  dprint("51." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(17, 59, 0, 2, 10, 2008))));
-  dprint("52." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(18, 00, 0, 2, 10, 2008))));
-  dprint("53." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
-  dprint("54." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(19, 0, 0, 2, 10, 2008))));
-  dprint("55." . (true  == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(9, 0, 0, 3, 10, 2008))));
-
-  dprint("End test (" . (microtime(true) - $start) . ")");
-}
-// Remove comment to run tests
-//test_elysia_cron_should_run();die();

+ 0 - 327
sites/all/modules/contrib/dev/elysia_cron/elysia_cron_update.php

@@ -1,327 +0,0 @@
-<?php
-
-/*******************************************************************************
- * ELYSIA CRON VERSION UPDATE
- ******************************************************************************/
-
-function elysia_cron_check_version_update() {
-  $ver = variable_get('elysia_cron_version', 0);
-  if ($ver < 20111012) {
-    $ver = _ec_variable_get('elysia_cron_version', 0);
-  }
-
-  if (!$ver || $ver < 20090218) {
-
-    $unchanged = array(
-      'elysia_cron_last_context',
-      'elysia_cron_last_run',
-      'elysia_cron_disabled',
-      'elysia_cron_semaphore',
-      'elysia_cron_key',
-      'elysia_cron_allowed_hosts',
-      'elysia_cron_default_rule',
-      'elysia_cron_script',
-      'elysia_cron_runtime_replacement',
-      'elysia_cron_version',
-    );
-
-    $rs = db_query("select * from {variable} where name like 'elysia_cron_%%'");
-    while ($v = db_fetch_object($rs)) {
-      if (!in_array($v->name, $unchanged)) {
-        $vn = false;
-        if (preg_match('/^elysia_cron_ctx_(.*)_(running|disabled|last_run|last_aborted|abort_count|execution_count|last_execution_time|avg_execution_time|max_execution_time|last_shutdown_time|last_abort_function)/', $v->name, $r)) {
-          switch ($r[2]) {
-            case 'running':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_r';
-              break;
-            case 'disabled':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_d';
-              break;
-            case 'last_run':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_lr';
-              break;
-            case 'last_aborted':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_la';
-              break;
-            case 'abort_count':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_ac';
-              break;
-            case 'execution_count':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_ec';
-              break;
-            case 'last_execution_time':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_let';
-              break;
-            case 'avg_execution_time':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_aet';
-              break;
-            case 'max_execution_time':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_met';
-              break;
-            case 'last_shutdown_time':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_lst';
-              break;
-            case 'last_abort_function':
-              $vn = 'ecc_' . _ec_get_name($r[1]) . '_laf';
-              break;
-          }
-        }
-        elseif (preg_match('/^elysia_cron_(.*)_(rule|disabled|context|running|last_run|last_execution_time|execution_count|avg_execution_time|max_execution_time)/', $v->name, $r)) {
-          switch ($r[2]) {
-            case 'rule':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_rul';
-              break;
-            case 'disabled':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_d';
-              break;
-            case 'context':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_c';
-              break;
-            case 'running':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_r';
-              break;
-            case 'last_run':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_lr';
-              break;
-            case 'last_execution_time':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_let';
-              break;
-            case 'execution_count':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_ec';
-              break;
-            case 'avg_execution_time':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_aet';
-              break;
-            case 'max_execution_time':
-              $vn = 'ec_' . _ec_get_name($r[1]) . '_met';
-              break;
-          }
-        }
-        if ($vn) {
-          variable_set($vn, unserialize($v->value));
-        }
-        else {
-          elysia_cron_error('Error in update, cant convert %name (value: %value)', array('%name' => $v->name, '%value' => $v->value), true);
-        }
-
-        variable_del($v->name);
-      }
-    }
-
-    variable_set('elysia_cron_version', 20090218);
-  }
-  if ($ver < 20090920) {
-    variable_set('elysia_cron_version', 20090920);
-
-  }
-  if ($ver < 20100507) {
-    if (EC_DRUPAL_VERSION >= 6) {
-      // D6
-      drupal_install_schema('elysia_cron');
-
-      // In ver 20111020 disabled has been renamed to disable, revert it now
-      if (EC_DRUPAL_VERSION >= 7) {
-        // D7
-        // Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
-        db_change_field($v = 'elysia_cron', 'disable', 'disabled', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
-
-      }
-      elseif (EC_DRUPAL_VERSION >= 6) {
-        // D6
-        $ret = array();
-        db_change_field($ret, 'elysia_cron', 'disable', 'disabled', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
-      }
-    }
-    else {
-      // D5
-      switch ($GLOBALS['db_type']) {
-        case 'mysqli':
-        case 'mysql':
-          db_query("create table if not exists {elysia_cron} (
-            name varchar(120) not null,
-            disabled tinyint(1) not null default '0',
-            rule varchar(32),
-            weight int(11) not null default '0',
-            context varchar(32),
-            running int(11) not null default '0',
-            last_run int(11) not null default '0',
-            last_aborted tinyint(1) not null default '0',
-            abort_count int(11) not null default '0',
-            last_abort_function varchar(32),
-            last_execution_time int(11) not null default '0',
-            execution_count int(11) not null default '0',
-            avg_execution_time float(5,2) not null default '0',
-            max_execution_time int(11) not null default '0',
-            last_shutdown_time int(11) not null default '0',
-            primary key (name)
-          )");
-          break;
-        case 'pgsql':
-          db_query("create table {elysia_cron} (
-            name varchar(120) not null,
-            disabled smallint not null default '0',
-            rule varchar(32),
-            weight integer not null default '0',
-            context varchar(32),
-            running int not null default '0',
-            last_run integer not null default '0',
-            last_aborted smallint not null default '0',
-            abort_count integer not null default '0',
-            last_abort_function varchar(32),
-            last_execution_time integer not null default '0',
-            execution_count integer not null default '0',
-            avg_execution_time float not null default '0',
-            max_execution_time integer not null default '0',
-            last_shutdown_time integer not null default '0',
-            primary key (name)
-          )");
-      }
-    }
-
-    $rs = db_query("select * from {variable} where name like 'ec_%%' or name like 'ecc_%%'");
-    $data = array();
-    $todelete = array();
-    while ($v = db_fetch_object($rs)) {
-      $name = false;
-      if (preg_match('/^ecc_(.+)_(r|d|lr|la|ac|ec|let|aet|met|lst|laf)/', $v->name, $r)) {
-        $name = ':' . $r[1];
-      }
-      elseif (preg_match('/^ec_(.+)_(rul|d|c|w|r|lr|let|ec|aet|met)/', $v->name, $r)) {
-        $name = $r[1];
-      }
-      if ($name) {
-        if (!isset($data[$name])) {
-          $data[$name] = array('name' => $name);
-        }
-        switch ($r[2]) {
-          case 'r':
-            $f = 'running';
-            break;
-          case 'd':
-            $f = 'disabled';
-            break;
-          case 'rul':
-            $f = 'rule';
-            break;
-          case 'w':
-            $f = 'weight';
-            break;
-          case 'c':
-            $f = 'context';
-            break;
-          case 'lr':
-            $f = 'last_run';
-            break;
-          case 'la':
-            $f = 'last_aborted';
-            break;
-          case 'ac':
-            $f = 'abort_count';
-            break;
-          case 'laf':
-            $f = 'last_abort_function';
-            break;
-          case 'let':
-            $f = 'last_execution_time';
-            break;
-          case 'ec':
-            $f = 'execution_count';
-            break;
-          case 'aet':
-            $f = 'avg_execution_time';
-            break;
-          case 'met':
-            $f = 'max_execution_time';
-            break;
-          case 'lst':
-            $f = 'last_shutdown_time';
-            break;
-        }
-        $data[$name][$f] = unserialize($v->value);
-        $todelete[] = $v->name;
-      }
-    }
-
-    $ifields = array('disabled', 'weight', 'running', 'last_run', 'last_aborted', 'abort_count', 'last_execution_time', 'execution_count', 'avg_execution_time', 'max_execution_time', 'last_shutdown_time');
-    foreach ($data as $v) {
-      foreach ($ifields as $f) {
-        if (empty($v[$f])) {
-          $v[$f] = 0;
-        }
-      }
-      db_query("insert into {elysia_cron} (name, disabled, rule, weight, context, running, last_run, last_aborted, abort_count, last_abort_function, last_execution_time, execution_count, avg_execution_time, max_execution_time, last_shutdown_time)
-        values ('%s', %d, '%s', %d, '%s', %d, %d, %d, %d, '%s', %d, %d, %f, %d, %d)",
-        $v['name'], $v['disabled'], $v['rule'], $v['weight'], $v['context'], $v['running'], $v['last_run'], $v['last_aborted'], $v['abort_count'], $v['last_abort_function'], $v['last_execution_time'], $v['execution_count'], $v['avg_execution_time'], $v['max_execution_time'], $v['last_shutdown_time']
-      );
-    }
-
-    db_query("update {elysia_cron} set context = null where context = ''");
-    db_query("update {elysia_cron} set rule = null where rule = ''");
-
-    foreach ($todelete as $v) {
-      variable_del($v);
-      db_query("DELETE FROM {variable} WHERE name = '%s'", $v);
-    }
-
-    variable_set('elysia_cron_version', 20100507);
-
-    unset($GLOBALS['_ec_variables']);
-  }
-  // D7 VERSION FROM NOW ON...
-
-  if ($ver < 20110323) {
-    if (EC_DRUPAL_VERSION >= 7) {
-      // D7
-      // Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
-      db_change_field($v = 'elysia_cron', 'weight', 'weight', array('type' => 'int', 'not null' => FALSE));
-
-    }
-    elseif (EC_DRUPAL_VERSION >= 6) {
-      // D6
-      $ret = array();
-      db_change_field($ret, 'elysia_cron', 'weight', 'weight', array('type' => 'int', 'not null' => FALSE));
-    }
-    else {
-      // D5
-      db_query("alter table {elysia_cron} change weight weight int(11)");
-    }
-
-    variable_set('elysia_cron_version', 20110323);
-  }
-  
-  if ($ver < 20111007) {
-    $default_rules = variable_get('elysia_cron_default_rules', $GLOBALS['elysia_cron_default_rules']);
-    if (!empty($default_rules['*/6 * * * *']) && $default_rules['*/6 * * * *'] == 'Every 6 hours') {
-      unset($default_rules['*/6 * * * *']);
-      $default_rules['0 */6 * * *'] = 'Every 6 hours';
-      variable_set('elysia_cron_default_rules', $default_rules);
-    }
-    variable_set('elysia_cron_version', 20111007);
-  }
-
-  if ($ver < 20111012) {
-    // I only need to rebuild variable cache, so i just set the new version
-    variable_set('elysia_cron_version', 20111012);
-  }
-
-  if ($ver < 20111020) {
-    if (EC_DRUPAL_VERSION >= 7) {
-      // D7
-      // Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
-      db_change_field($v = 'elysia_cron', 'disabled', 'disable', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
-
-    }
-    elseif (EC_DRUPAL_VERSION >= 6) {
-      // D6
-      $ret = array();
-      db_change_field($ret, 'elysia_cron', 'disabled', 'disable', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
-    }
-    else {
-      // D5
-      db_query("alter table {elysia_cron} change disabled disable tinyint(1)");
-    }
-    db_query("update {elysia_cron} set disable = NULL where disable = 0");
-
-    variable_set('elysia_cron_version', 20111020);
-  }
-}

+ 0 - 86
sites/all/modules/contrib/dev/elysia_cron/elysia_drupalconv.php

@@ -1,86 +0,0 @@
-<?php
-
-define('EC_DRUPAL_VERSION', 7);
-
-  /***************************************************************
-   * D7 VERSION 
-   ***************************************************************/
-
-  function _dcf_hook_boot($module) {
-    return true;
-  }
-  
-  function _dcf_hook_init($module) {
-    return true;
-  }
-    
-  function _dcf_hook_menu($items, $maycache) {
-    return $items;
-  }
-  
-  function _dcr_render_array($output) {
-    return $output;
-  }
-  
-  function _dcr_form(&$form) {
-    return $form;
-  }
-  
-  function _dcf_internal_path($path) {
-    return $path;
-  }
-  
-  function _dcf_t($string) {
-    return $string;
-  }
-  
-  function _dco_watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { // WARN d7 changed WATCHDOG_ costants
-    return watchdog($type, $message, $variables, $severity, $link);
-  }
-  
-  function _dco_l($text, $path, array $options = array()) {
-    return l($text, $path, $options);
-  }
-  
-  function _dcf_form_validate(&$form, &$form_state) {
-    return array('form' => &$form, 'form_state' => &$form_state);
-  }
-  
-  function _dco_theme($name, $args) {
-    return theme($name, $args);
-  }
-  
-  function _dcf_theme_signature($args) {
-    return array();
-  }
-  
-  function _dcr_hook_theme($specs) { 
-    return $specs;
-  }
-
-  function _dcf_theme_form(&$args) {
-    return array( 'variables' => $args );
-  }
-
-  /***************************************************************
-   * D7 EXTRA FUNCTIONS 
-   ***************************************************************/
-  
-  function drupal_module_get_min_weight($except_module = false) {
-    return !$except_module ? db_query("select min(weight) from {system}")->fetchField() :
-      db_query("select min(weight) from {system} where name != :name", array(':name' => $except_module))->fetchField();
-  }
-  
-  function drupal_module_get_weight($name) {
-    return db_query("select weight from {system} where name = :name", array(':name' => $name))->fetchField();  
-  }
-  
-  function drupal_module_set_weight($name, $weight) {
-    db_update('system')->fields(array('weight' => $weight))->condition('name', $name)->execute();
-  }
-  
-  function drupal_disable_standard_cron() {
-  }
-  
-  function drupal_clean_after_cron_run() {
-  }

+ 30 - 28
sites/all/modules/contrib/flag/flag/flag.api.php

@@ -15,7 +15,7 @@
  *
  * This hook may be placed in a $module.flag.inc file.
  *
- * @return
+ * @return array
  *  An array whose keys are flag type names and whose values are properties of
  *  the flag type.
  *  When a flag type is for an entity, the flag type name must match the entity
@@ -46,7 +46,7 @@ function hook_flag_type_info() {
  *
  * This hook may be placed in a $module.flag.inc file.
  *
- * @param $definitions
+ * @param array $definitions
  *  An array of flag definitions returned by hook_flag_type_info().
  */
 function hook_flag_type_info_alter(&$definitions) {
@@ -118,10 +118,10 @@ function hook_flag_alter(&$flag) {
  * them here so that their additions to the flag admin form are saved into the
  * flag object.
  *
- * @param $options
+ * @param array $options
  *  The array of default options for the flag type, with the options for the
  *  flag's link type merged in.
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
  *
  * @see flag_flag::options()
@@ -133,9 +133,9 @@ function hook_flag_options_alter(&$options, $flag) {
 /**
  * Act on an object being flagged.
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
- * @param $entity_id
+ * @param int $entity_id
  *  The id of the entity the flag is on.
  * @param $account
  *  The user account performing the action.
@@ -154,7 +154,7 @@ function hook_flag_flag($flag, $entity_id, $account, $flagging) {
  *
  * @param $flag
  *  The flag object.
- * @param $entity_id
+ * @param int $entity_id
  *  The id of the entity the flag is on.
  * @param $account
  *  The user account performing the action.
@@ -168,18 +168,18 @@ function hook_flag_unflag($flag, $entity_id, $account, $flagging) {
 /**
  * Perform custom validation on a flag before flagging/unflagging.
  *
- * @param $action
+ * @param string $action
  *  The action about to be carried out. Either 'flag' or 'unflag'.
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
- * @param $entity_id
+ * @param int $entity_id
  *  The id of the entity the user is trying to flag or unflag.
  * @param $account
  *  The user account performing the action.
  * @param $flagging
  *  The flagging entity.
  *
- * @return
+ * @return array|NULL
  *   Optional array: textual error with the error-name as the key.
  *   If the error name is 'access-denied' and javascript is disabled,
  *   drupal_access_denied will be called and a 403 will be returned.
@@ -207,11 +207,11 @@ function hook_flag_validate($action, $flag, $entity_id, $account, $skip_permissi
  * Called when displaying a single entity view or edit page.  For flag access
  * checks from within Views, implement hook_flag_access_multiple().
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
  * @param $entity_id
  *  The id of the entity in question.
- * @param $action
+ * @param string $action
  *  The action to test. Either 'flag' or 'unflag'.
  * @param $account
  *  The user on whose behalf to test the flagging action.
@@ -238,16 +238,18 @@ function hook_flag_access($flag, $entity_id, $action, $account) {
  * Called when preparing a View or list of multiple flaggable entities.
  * For flag access checks for individual entities, see hook_flag_access().
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
- * @param $entity_ids
+ * @param array $entity_ids
  *  An array of object ids to check access.
  * @param $account
  *  The user on whose behalf to test the flagging action.
  *
- * @return
+ * @return array
  *   An array whose keys are the object IDs and values are booleans indicating
- *   access.
+ *   access: TRUE to grant access, FALSE to deny it, and NULL to leave the core
+ *   access unchanged. If the implementation does not wish to override any
+ *   access, an empty array may be returned.
  *
  * @see hook_flag_access()
  * @see flag_flag:access_multiple()
@@ -291,7 +293,7 @@ function hook_flag_link_type_info() {
  *
  * This hook may be placed in a $module.flag.inc file.
  *
- * @param $link_types
+ * @param array $link_types
  *  An array of the link types defined by all modules.
  *
  * @see flag_get_link_types()
@@ -311,11 +313,11 @@ function hook_flag_link_type_info_alter(&$link_types) {
  * attributes, using the same structure as hook_link(). Note that "title" is
  * provided by the Flag configuration if not specified here.
  *
- * @param $flag
+ * @param flag_flag $flag
  *   The full flag object for the flag link being generated.
- * @param $action
+ * @param string $action
  *   The action this link should perform. Either 'flag' or 'unflag'.
- * @param $entity_id
+ * @param int $entity_id
  *   The ID of the node, comment, user, or other object being flagged. The type
  *   of the object can be deduced from the flag type.
  *
@@ -325,7 +327,7 @@ function hook_flag_link_type_info_alter(&$link_types) {
  * @see hook_flag_link_type_info()
  * @see template_preprocess_flag()
  */
-function hook_flag_link() {
+function hook_flag_link($flag, $action, $entity_id) {
 
 }
 
@@ -335,7 +337,7 @@ function hook_flag_link() {
  * This is invoked after all the flag database tables have had their relevant
  * entries deleted.
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object that has been deleted.
  */
 function hook_flag_delete($flag) {
@@ -345,9 +347,9 @@ function hook_flag_delete($flag) {
 /**
  * Act when a flag is reset.
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
- * @param $entity_id
+ * @param int $entity_id
  *  The entity ID on which all flaggings are to be removed. May be NULL, in
  *  which case all of this flag's entities are to be unflagged.
  * @param $rows
@@ -362,9 +364,9 @@ function hook_flag_reset($flag, $entity_id, $rows) {
 /**
  * Alter the javascript structure that describes the flag operation.
  *
- * @param $info
+ * @param array $info
  *   The info array before it is returned from flag_build_javascript_info().
- * @param $flag
+ * @param flag_flag $flag
  *   The full flag object.
  *
  * @see flag_build_javascript_info()
@@ -384,7 +386,7 @@ function hook_flag_javascript_info_alter(&$info, $flag) {
 /**
  * Alter a flag object that is being prepared for exporting.
  *
- * @param $flag
+ * @param flag_flag $flag
  *  The flag object.
  *
  * @see flag_export_flags()

+ 1 - 1
sites/all/modules/contrib/flag/flag/flag.flag.inc

@@ -10,7 +10,7 @@
  *
  * Defines the flag types this module implements.
  *
- * @return
+ * @return array
  *   An "array of arrays", keyed by object type. The 'handler' slot
  *   should point to the PHP class implementing this flag.
  */

+ 3 - 3
sites/all/modules/contrib/flag/flag/flag.info

@@ -29,9 +29,9 @@ files[] = includes/views/flag_handler_relationships.inc
 files[] = includes/views/flag_plugin_argument_validate_flaggability.inc
 files[] = tests/flag.test
 
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 9 - 37
sites/all/modules/contrib/flag/flag/flag.install

@@ -22,7 +22,7 @@ function flag_schema() {
         'not null' => TRUE,
       ),
       'entity_type' => array(
-        'description' => 'The flag type, such as one of "node", "comment", or "user".',
+        'description' => 'The flag type, for example "node", "comment", or "user".',
         'type' => 'varchar',
         'length' => '128',
         'not null' => TRUE,
@@ -79,14 +79,14 @@ function flag_schema() {
         'default' => 0,
       ),
       'entity_type' => array(
-        'description' => 'The flag type, eg "node", "comment", "user".',
+        'description' => 'The flag type, for example "node", "comment", or "user".',
         'type' => 'varchar',
         'length' => '128',
         'not null' => TRUE,
         'default' => '',
       ),
       'entity_id' => array(
-        'description' => 'The unique ID of the object, such as either the {cid}, {uid}, or {nid}.',
+        'description' => 'The unique ID of the flagged entity, for example the uid, cid, or nid.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -166,14 +166,14 @@ function flag_schema() {
         'default' => 0,
       ),
       'entity_type' => array(
-        'description' => 'The flag type, usually one of "node", "comment", "user".',
+        'description' => 'The flag type, for example "node", "comment", or "user".',
         'type' => 'varchar',
         'length' => '128',
         'not null' => TRUE,
         'default' => '',
       ),
       'entity_id' => array(
-        'description' => 'The unique ID of the content, usually either the {cid}, {uid}, or {nid}.',
+        'description' => 'The unique ID of the flagged entity, for example the uid, cid, or nid.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -508,30 +508,14 @@ function flag_update_7303() {
       'not null' => TRUE,
       'default' => '',
     ),
-    // Keys spec. Some are short-lived, as they are about to be dropped again
-    // and have hybrid names that refer to 'content_id' still.
+    // Keys spec.
     array(
-      'unique keys' => array(
-        'fid_content_id_uid_sid' => array('fid', 'content_id', 'uid', 'sid'),
-      ),
       'indexes' => array(
         'entity_type_uid_sid' => array('entity_type', 'uid', 'sid'),
-        'entity_type_content_id_uid_sid' => array(
-          'entity_type',
-          'content_id',
-          'uid',
-          'sid',
-        ),
-        'content_id_fid' => array('content_id', 'fid'),
       ),
     )
   );
 
-  // Now we have to drop keys and indexes all over again!
-  db_drop_unique_key('flagging', 'fid_content_id_uid_sid');
-  db_drop_index('flagging', 'entity_type_content_id_uid_sid');
-  db_drop_index('flagging', 'content_id_fid');
-
   // Change field 'content_id' to 'entity_id'.
   db_change_field('flagging', 'content_id', 'entity_id',
     // Spec of the field. Identical to our current hook_schema(): we're not
@@ -542,23 +526,11 @@ function flag_update_7303() {
       'unsigned' => TRUE,
       'not null' => TRUE,
       'default' => 0,
-    ),
-    // Keys spec. Identical to current hook_schema().
-    array(
-      'unique keys' => array(
-        'fid_entity_id_uid_sid' => array('fid', 'entity_id', 'uid', 'sid'),
-      ),
-      'indexes' => array(
-        'entity_type_entity_id_uid_sid' => array(
-          'entity_type',
-          'entity_id',
-          'uid',
-          'sid',
-        ),
-        'entity_id_fid' => array('entity_id', 'fid'),
-      ),
     )
   );
+  db_add_unique_key('flagging', 'fid_entity_id_uid_sid', array('fid', 'entity_id', 'uid', 'sid'));
+  db_add_index('flagging', 'entity_type_entity_id_uid_sid', array('entity_type', 'entity_id', 'uid', 'sid'));
+  db_add_index('flagging', 'entity_id_fid', array('entity_id', 'fid'));
 
   // A serial field must be defined as a key, so make a temporary index on
   // 'fcid' so we can safely drop the primary key.

+ 116 - 46
sites/all/modules/contrib/flag/flag/flag.module

@@ -37,20 +37,25 @@ function flag_entity_info() {
     ),
   );
 
-  // Add bundle info but bypass flag_get_flags() as we cannot use it here, as
-  // it calls entity_get_info().
-  $result = db_query("SELECT name, title FROM {flag}");
-  $flag_names = $result->fetchAllKeyed();
-  foreach ($flag_names as $flag_name => $flag_title) {
-    $return['flagging']['bundles'][$flag_name] = array(
-      'label' => $flag_title,
-      'admin' => array(
-        'path' => FLAG_ADMIN_PATH . '/manage/%flag',
-        'real path' => FLAG_ADMIN_PATH . '/manage/' . $flag_name,
-        'bundle argument' => FLAG_ADMIN_PATH_START + 1,
-        'access arguments' => array('administer flags'),
-      ),
-    );
+  // Check for our table before we query it. This is a workaround for a core
+  // bug: https://www.drupal.org/node/1311820
+  // TODO: Remove this when that bug is fixed.
+  if (db_table_exists('flag')) {
+    // Add bundle info but bypass flag_get_flags() as we cannot use it here, as
+    // it calls entity_get_info().
+    $result = db_query("SELECT name, title FROM {flag}");
+    $flag_names = $result->fetchAllKeyed();
+    foreach ($flag_names as $flag_name => $flag_title) {
+      $return['flagging']['bundles'][$flag_name] = array(
+        'label' => $flag_title,
+        'admin' => array(
+          'path' => FLAG_ADMIN_PATH . '/manage/%flag',
+          'real path' => FLAG_ADMIN_PATH . '/manage/' . $flag_name,
+          'bundle argument' => FLAG_ADMIN_PATH_START + 1,
+          'access arguments' => array('administer flags'),
+        ),
+      );
+    }
   }
 
   return $return;
@@ -59,12 +64,12 @@ function flag_entity_info() {
 /**
  * Loads a flagging entity.
  *
- * @param $flagging_id
+ * @param int $flagging_id
  *   The 'flagging_id' database serial column.
- * @param $reset
+ * @param bool $reset
  *   Whether to reset the DrupalDefaultEntityController cache.
  *
- * @return
+ * @return stdClass
  *   The entity object, or FALSE if it can't be found.
  */
 function flagging_load($flagging_id, $reset = FALSE) {
@@ -78,7 +83,7 @@ function flagging_load($flagging_id, $reset = FALSE) {
  *
  * Creates an unsaved flagging object for use with $flag->flag().
  *
- * @param $values
+ * @param array $values
  *   An array of values as described by the entity's property info. Only
  *   'flag_name' or 'fid' must be specified, since $flag->flag() does the rest.
  *
@@ -149,12 +154,6 @@ function flagging_save($flagging) {
 
 // @todo: Implement flagging_view(). Not extremely useful. I already have it.
 
-// @todo: When renaming a flag: Call field_attach_rename_bundle().
-
-// @todo: When creating a flag: Call field_attach_create_bundle().
-
-// @todo: When deleting a flag: Call field_attach_delete_bundle().
-
 // @tood: Discuss: Should flag deleting call flag_reset_flag()? No.
 
 // @todo: flag_reset_flag():
@@ -199,6 +198,21 @@ function flag_entity_query_alter(EntityFieldQuery $query) {
  * @see flag_entity_query_alter()
  */
 function flag_query_flagging_flag_names_alter(QueryAlterableInterface $query) {
+  // Queries with this tag need to have the {flag} table joined on so they can
+  // have a condition on the flag name.
+  // However, we need to ensure the {flagging} table is there to join from. Not
+  // all instances of EntityFieldQuery will add it; for example, with only a
+  // field condition the entity base table is not added to the SelectQuery.
+  $tables =& $query->getTables();
+  if (!isset($tables['flagging'])) {
+    // All tables that are in the query will be field tables and are equivalent,
+    // so just join on the first one.
+    $field_table = reset($tables);
+    $field_table_alias = $field_table['alias'];
+
+    $query->join('flagging', 'flagging', "$field_table_alias.entity_id = flagging.fid");
+  }
+
   // Get value and operator for bundle condition from meta data.
   $value = $query->getMetaData('flag_name_value');
   $operator = $query->getMetaData('flag_name_operator');
@@ -261,7 +275,6 @@ function flag_menu() {
     'access callback' => 'user_access',
     'access arguments' => array('administer flags'),
     'file' => 'includes/flag.admin.inc',
-    'type' => MENU_LOCAL_TASK,
     // Make the flag title the default title for descendant menu items.
     'title callback' => '_flag_menu_title',
     'title arguments' => array(FLAG_ADMIN_PATH_START + 1),
@@ -306,8 +319,8 @@ function flag_menu() {
     'title' => 'Flag',
     'page callback' => 'flag_page',
     'page arguments' => array(1, 2, 3),
-    'access callback' => 'user_access',
-    'access arguments' => array('access content'),
+    'access callback' => 'flag_page_access',
+    'access arguments' => array(1, 2, 3),
     'file' => 'includes/flag.pages.inc',
     'type' => MENU_CALLBACK,
   );
@@ -315,8 +328,8 @@ function flag_menu() {
     'title' => 'Flag confirm',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('flag_confirm', 2, 3, 4),
-    'access callback' => 'user_access',
-    'access arguments' => array('access content'),
+    'access callback' => 'flag_page_access',
+    'access arguments' => array(2, 3, 4),
     'file' => 'includes/flag.pages.inc',
     'type' => MENU_CALLBACK,
   );
@@ -324,6 +337,19 @@ function flag_menu() {
   return $items;
 }
 
+/**
+ * Menu access callback for flagging pages.
+ *
+ * Same parameters as flag_page().
+ *
+ * @see flag_page()
+ * @see flag_confirm()
+ */
+function flag_page_access($action, $flag, $entity_id) {
+  $access = $flag->access($entity_id, $action);
+  return $access;
+}
+
 /**
  * Implements hook_admin_menu_map().
  */
@@ -348,9 +374,9 @@ function flag_admin_menu_map() {
 /**
  * Menu loader for '%flag' arguments.
  *
- * @param $flag_name
+ * @param string $flag_name
  *   The machine name of the flag.
- * @param $include_disabled
+ * @param bool $include_disabled
  *   (optional) Whether to return a disabled flag too. Normally only enabled
  *   flags are returned. Some menu items operate on disabled flags and in this
  *   case you need to turn on this switch by doing:
@@ -489,7 +515,7 @@ function flag_hook_info() {
 /**
  * Get a flag type definition.
  *
- * @param $entity_type
+ * @param string $entity_type
  *   (optional) The entity type to get the definition for, or NULL to return
  *   all flag types.
  *
@@ -1386,7 +1412,7 @@ function flag_flag_access($flag, $entity_id, $action, $account) {
   if ($flag->entity_type == 'comment') {
     // For non-existent comments (such as on the comment add form), assume that
     // the current user is creating the content.
-    if (empty($entity_id) || !($comment = $flag->fetch_entity($entity_id))) {
+    if (empty($entity_id) || !($comment = $flag->fetch_entity($entity_id)) || $entity_id == 'new') {
       return $flag->access_author == 'comment_others' ? FALSE : NULL;
     }
 
@@ -1676,7 +1702,11 @@ function _flag_link_type_descriptions() {
 // Non-Views public API
 
 /**
- * Get the count of flags for a particular entity type.
+ * Gets the count of flaggings for the given flag.
+ *
+ * For example, if you have an 'endorse' flag, this method will tell you how
+ * many endorsements have been made, rather than how many things have been
+ * endorsed.
  *
  * When called during a flagging or unflagging (such as from a hook
  * implementation or from Rules), the flagging or unflagging that is in the
@@ -1692,8 +1722,8 @@ function _flag_link_type_descriptions() {
  * @param $entity_type
  *   The entity type. For example, 'node'.
  *
- * @return
- *   The flag count with the flag name and entity type as the array key.
+ * @return int
+ *   The number of flaggings for the flag.
  */
 function flag_get_entity_flag_counts($flag, $entity_type) {
   $counts = &drupal_static(__FUNCTION__);
@@ -1716,7 +1746,10 @@ function flag_get_entity_flag_counts($flag, $entity_type) {
 }
 
 /**
- * Get the user's flag count.
+ * Gets the count of the flaggings made by a user with a flag.
+ *
+ * For example, with a 'bookmarks' flag, this returns the number of bookmarks
+ * a user has created.
  *
  * When called during a flagging or unflagging (such as from a hook
  * implementation or from Rules), the flagging or unflagging that is in the
@@ -1727,13 +1760,15 @@ function flag_get_entity_flag_counts($flag, $entity_type) {
  * This is because this queries the {flagging} table, which only has its record
  * deleted at the very end of the unflagging process.
  *
+ * However, it should be noted that this method does not serves global flags.
+ *
  * @param $flag
  *   The flag.
  * @param $user
  *   The user object.
  *
- * @return
- *   The flag count with the flag name and the uid as the array key.
+ * @return int
+ *   The number of flaggings for the given flag and user.
  */
 function flag_get_user_flag_counts($flag, $user) {
   $counts = &drupal_static(__FUNCTION__);
@@ -1756,7 +1791,18 @@ function flag_get_user_flag_counts($flag, $user) {
 }
 
 /**
- * Get flag counts for all flags on a node.
+ * Gets flag counts for all flags on an entity.
+ *
+ * Provides a count of all the flaggings for a single entity. Instead
+ * of a single response, this method returns an array of counts keyed by
+ * the flag ID:
+ *
+ * @code
+ * array(
+ *   my_flag => 42
+ *   another_flag => 57
+ * );
+ * @endcode
  *
  * When called during a flagging or unflagging (such as from a hook
  * implementation or from Rules), the count this returns takes into account the
@@ -1767,8 +1813,10 @@ function flag_get_user_flag_counts($flag, $user) {
  * @param $entity_id
  *   The entity ID (usually the node ID).
  *
- * @return
- *   The flag count with the flag names as array keys.
+ * @return array
+ *   An array giving the counts of all flaggings on the entity. The flag IDs
+ *   are the keys and the counts for each flag the values. Note that flags
+ *   that have no flaggings are not included in the array.
  */
 function flag_get_counts($entity_type, $entity_id) {
   $counts = &drupal_static(__FUNCTION__);
@@ -1792,7 +1840,12 @@ function flag_get_counts($entity_type, $entity_id) {
 }
 
 /**
- * Get the total count of items flagged within a flag.
+ * Gets the count of entities flagged by the given flag.
+ *
+ * For example, with a 'report abuse' flag, this returns the number of
+ * entities that have been reported, not the total number of reports. In other
+ * words, an entity that has been reported multiple times will only be counted
+ * once.
  *
  * When called during a flagging or unflagging (such as from a hook
  * implementation or from Rules), the count this returns takes into account the
@@ -1802,6 +1855,9 @@ function flag_get_counts($entity_type, $entity_id) {
  *   The flag name for which to retrieve a flag count.
  * @param $reset
  *   (optional) Reset the internal cache and execute the SQL query another time.
+ *
+ * @return int
+ *   The number of entities that are flagged with the flag.
  */
 function flag_get_flag_counts($flag_name, $reset = FALSE) {
   $counts = &drupal_static(__FUNCTION__);
@@ -1853,8 +1909,7 @@ function flag_get_flag($name = NULL, $fid = NULL) {
 /**
  * List all flags available.
  *
- * If node type or account are entered, a list of all possible flags will be
- * returned.
+ * If all the parameters are omitted, a list of all flags will be returned.
  *
  * @param $entity_type
  *   (optional) The type of entity for which to load the flags. Usually 'node'.
@@ -1865,7 +1920,7 @@ function flag_get_flag($name = NULL, $fid = NULL) {
  *   flags for will this node will be returned.
  *
  * @return
- *   An array of the structure [fid] = flag_object.
+ *   An array of flag objects, keyed by the flag names.
  */
 function flag_get_flags($entity_type = NULL, $content_subtype = NULL, $account = NULL) {
   $flags = &drupal_static(__FUNCTION__);
@@ -2545,6 +2600,21 @@ function flag_ctools_plugin_directory($module, $plugin) {
   }
 }
 
+/**
+ * Implements hook_field_attach_rename_bundle().
+ */
+function flag_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+  $flags = flag_get_flags($entity_type);
+  foreach ($flags as $flag) {
+    foreach ($flag->types as $key => $type) {
+      if ($type == $bundle_old) {
+        $flag->types[$key] = $bundle_new;
+      }
+    }
+    $flag->save();
+  }
+}
+
 // ---------------------------------------------------------------------------
 // Entity Metadata callbacks
 

+ 7 - 2
sites/all/modules/contrib/flag/flag/flag.tokens.inc

@@ -83,6 +83,11 @@ function flag_token_info() {
   if (module_exists('token')) {
     $entity_info = entity_get_info();
     foreach (flag_get_types() as $flag_type) {
+      // If the flag type is not an entity type then skip.
+      if (!isset($entity_info[$flag_type])) {
+        continue;
+      }
+
       // The flag type is the entity type, but this is not necessarily the same
       // as the entity's token type.
       $token_type = $entity_info[$flag_type]['token type'];
@@ -201,7 +206,7 @@ function flag_tokens($type, $tokens, array $data = array(), array $options = arr
 /**
  * Returns HTML for a tokens browser.
  *
- * @param $variables
+ * @param array $variables
  *   An associative array containing:
  *   - types: An array naming the types of tokens to show.
  *   - global_types: Whether to show global tokens.
@@ -211,7 +216,7 @@ function theme_flag_tokens_browser($variables) {
   $global_types = $variables['global_types'];
 
   if (module_exists('token')) {
-    return theme('token_tree', array('token_types' => $types, 'global_types' => $global_types));
+    return theme('token_tree', array('token_types' => $types, 'global_types' => $global_types, 'dialog' => TRUE));
   }
   else {
     return '<p><em>' . t("Note: You don't have the <a href='@token-url'>Token</a> module installed, so the list of available tokens isn't shown here. You don't have to install <a href='@token-url'>Token</a> to be able to use tokens, but if you have it installed, and enabled, you'll be able to enjoy an interactive tokens browser.", array('@token-url' => 'http://drupal.org/project/token')) . '</em></p>';

+ 3 - 5
sites/all/modules/contrib/flag/flag/flag_actions.info

@@ -5,11 +5,9 @@ dependencies[] = flag
 package = Flags
 configure = admin/structure/flags/actions
 
-files[] = flag.install
-files[] = flag_actions.module
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 14 - 14
sites/all/modules/contrib/flag/flag/flag_actions.module

@@ -136,17 +136,17 @@ function flag_actions_get_actions($flag_name = NULL, $reset = FALSE) {
 /**
  * Insert a new flag action.
  *
- * @param $fid
+ * @param int $fid
  *   The flag object ID.
- * @param $event
+ * @param string $event
  *   The flag event, such as "flag" or "unflag".
- * @param $threshold
+ * @param int $threshold
  *   The flagging threshold at which this action will be executed.
- * @param $repeat_threshold
+ * @param int $repeat_threshold
  *   The number of additional flaggings after which the action will be repeated.
- * @param $callback
+ * @param string $callback
  *   The action callback to be executed.
- * @param $parameters
+ * @param array $parameters
  *   The action parameters.
  */
 function flag_actions_insert_action($fid, $event, $threshold, $repeat_threshold, $callback, $parameters) {
@@ -165,15 +165,15 @@ function flag_actions_insert_action($fid, $event, $threshold, $repeat_threshold,
 /**
  * Update an existing flag action.
  *
- * @param $aid
+ * @param int $aid
  *   The flag action ID to update.
- * @param $event
+ * @param string $event
  *   The flag event, such as "flag" or "unflag".
- * @param $threshold
+ * @param int $threshold
  *   The flagging threshold at which this action will be executed.
- * @param $repeat_threshold
+ * @param int $repeat_threshold
  *   The number of additional flaggings after which the action will be repeated.
- * @param $parameters
+ * @param array $parameters
  *   The action parameters.
  */
 function flag_actions_update_action($aid, $event, $threshold, $repeat_threshold, $parameters) {
@@ -191,7 +191,7 @@ function flag_actions_update_action($aid, $event, $threshold, $repeat_threshold,
 /**
  * Delete a flag action.
  *
- * @param $aid
+ * @param int $aid
  *   The flag action ID to delete.
  */
 function flag_actions_delete_action($aid) {
@@ -393,9 +393,9 @@ function theme_flag_actions_add_form($variables) {
  *
  * @param $form_state
  *   The form state.
- * @param $aid
+ * @param int|NULL $aid
  *   If editing an action, an action ID must be passed in.
- * @param $flag_name
+ * @param string|NULL $flag_name
  *   If adding a new action to a flag, a flag name must be specified.
  */
 function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL) {

+ 4 - 3
sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.info

@@ -3,10 +3,11 @@ description = Provides an example bookmark flag and supporting views.
 core = 7.x
 dependencies[] = flag
 package = Flags
+files[] = plugins/flag_bookmark_plugin_validate_user.inc
 
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 21 - 0
sites/all/modules/contrib/flag/flag/flag_bookmark/includes/flag_bookmark.views.inc

@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Views plugins for the flag bookmark module.
+ */
+
+/**
+ * Implements hook_views_plugins().
+ */
+function flag_bookmark_views_plugins() {
+  return array(
+    'argument validator' => array(
+      'flag_bookmark_plugin_owner_perm' => array(
+        'title' => t("Current user ID matches argument value"),
+        'handler' => 'flag_bookmark_plugin_validate_user',
+        'path' => drupal_get_path('module', 'flag_bookmark') . '/plugins',
+      ),
+    ),
+  );
+}

+ 4 - 3
sites/all/modules/contrib/flag/flag/flag_bookmark/includes/flag_bookmark.views_default.inc

@@ -168,12 +168,13 @@ function flag_bookmark_views_default_views() {
       'default_action' => 'empty',
       'style_plugin' => 'default_summary',
       'style_options' => array(),
-      'wildcard' => 'all',
-      'wildcard_substitution' => 'All',
+      'exception' => array(
+        'value' => '',
+      ),
       'title' => '%1\'s bookmarks',
       'default_argument_type' => 'fixed',
       'default_argument' => '',
-      'validate_type' => 'none',
+      'validate_type' => 'flag_bookmark_plugin_owner_perm',
       'validate_fail' => 'not found',
       'break_phrase' => 0,
       'not' => 0,

+ 89 - 0
sites/all/modules/contrib/flag/flag/flag_bookmark/plugins/flag_bookmark_plugin_validate_user.inc

@@ -0,0 +1,89 @@
+<?php
+/**
+ * @file
+ * Contains the Flag Bookmark view argument validator.
+ */
+
+/**
+ * Validates whether an argument is a valid UID.
+ *
+ * @ingroup views
+ */
+class flag_bookmark_plugin_validate_user extends views_plugin_argument_validate_user {
+
+  /**
+   * Define the options for the plugin, including the default permission.
+   * @return multitype:string
+   */
+  function option_definition() {
+    // Initialize the base class.
+    $options = parent::option_definition();
+
+    // Set the default permission.
+    $options['bypass_perm'] = array('default' => 'administer users');
+
+    return $options;
+  }
+
+  /**
+   * Returns a option form for the plugin.
+   */
+  function options_form(&$form, &$form_state) {
+    // Get the options form from the base class.
+    parent::options_form($form, $form_state);
+
+    $perms = array();
+    $module_info = system_get_info('module');
+
+    $perms[] = t(' - None - ');
+
+    // Produce an array of permissions keyed by module name.
+    foreach (module_implements('permission') as $module) {
+      $permissions = module_invoke($module, 'permission');
+      foreach ($permissions as $name => $perm) {
+        $perms[$module_info[$module]['name']][$name] = strip_tags($perm['title']);
+      }
+    }
+
+    asort($perms);
+
+    // Create the form field for the validator. Returned by reference.
+    $form['bypass_perm'] = array(
+      '#type' => 'select',
+      '#options' => $perms,
+      '#title' => t('Override permission'),
+      '#default_value' => $this->options['bypass_perm'],
+      '#description' => t('Users with this permission bypass the argument check and are granted access.'),
+    );
+  }
+
+  /**
+   * Validates the argument to be a proper UID.
+   * @param mixed $argument
+   * @return boolean
+   */
+  function validate_argument($argument) {
+    // The parent class takes care of all its options, returning TRUE if the
+    // argument value validates to a user account, and an account that has the
+    // required role.
+    $argument_validates = parent::validate_argument($argument);
+
+    // If the parent didn't validate the argument, then we certainly can't
+    // either.
+    if ($argument_validates == FALSE) {
+      return $argument_validates;
+    }
+
+    // If the current user has the bypass permission, then we're done: return
+    // the validation status we got from the parent.
+    if (!empty($this->options['bypass_perm']) && user_access($this->options['bypass_perm'])) {
+      return $argument_validates;
+    }
+
+    // Otherwise, perform our additional check to enforce that the argument
+    // user ID is the current user.
+    // The parent method has stored the uid from the argument.
+    return ($this->argument->argument == $GLOBALS['user']->uid);
+  }
+
+}

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag.actions.inc

@@ -179,7 +179,7 @@ function flag_user_action_submit($form, $form_state) {
  *
  * @param $context
  *   The current action context.
- * @param $entity_type
+ * @param string $entity_type
  *   The entity type applicable to this action, such as "node" or "comment".
  */
 function flag_action_form($context, $entity_type) {

+ 12 - 5
sites/all/modules/contrib/flag/flag/includes/flag.admin.inc

@@ -232,7 +232,7 @@ function theme_flag_admin_page($variables) {
 /**
  * Menu callback for adding a new flag.
  *
- * @param $entity_type
+ * @param string|NULL $entity_type
  *  The entity type for the new flag, taken from the path argument. If not
  *  present (i.e., '/add'), a form showing all possible flag types is shown.
  *  Otherwise, this shows a form for adding af flag the given type.
@@ -401,7 +401,7 @@ function flag_form($form, &$form_state, $flag) {
         t('Add this [node:type] to your favorites'),
         t('Vote for this proposal ([node:flag-vote-count] people have already done so)'),
       ),
-      'attributes' => array('class' => 'token-examples'),
+      'attributes' => array('class' => array('token-examples')),
     )) .
     '<p>' . t('These tokens will be replaced with the appropriate fields from the node (or user, or comment).') . '</p>' .
     theme('flag_tokens_browser', array('types' => $flag->get_labels_token_types())),
@@ -693,19 +693,26 @@ function flag_form_submit($form, &$form_state) {
   // We clear caches more vigorously if the flag was new.
   _flag_clear_cache($flag->entity_type, !empty($flag->is_new));
 
+  if (!empty($flag->is_new)) {
+    field_attach_create_bundle('flagging', $flag->name);
+  }
+
   // Save permissions.
   // This needs to be done after the flag cache has been cleared, so that
   // the new permissions are picked up by hook_permission().
   // This may need to move to the flag class when we implement extra permissions
   // for different flag types: http://drupal.org/node/879988
 
-  // If the flag machine name as changed, clean up all the obsolete permissions.
+  // If the flag machine name as changed, clean up all the obsolete permissions
+  // and notify FieldAPI.
   if ($flag->name != $form['#flag_name']) {
     $old_name = $form['#flag_name'];
     $permissions = array("flag $old_name", "unflag $old_name");
     foreach (array_keys(user_roles()) as $rid) {
       user_role_revoke_permissions($rid, $permissions);
     }
+
+    field_attach_rename_bundle('flagging', $old_name, $flag->name);
   }
 
   foreach (array_keys(user_roles(!module_exists('session_api'))) as $rid) {
@@ -796,9 +803,9 @@ function flag_check_link_types($element) {
 /**
  * Clears various caches when one or more flags are modified.
  *
- * @param $entity_types
+ * @param string|array $entity_types
  *  The entity types for the flags. May be a single value or an array.
- * @param $is_insert_or_delete
+ * @param bool $is_insert_or_delete
  *  Whether the modified flag is being inserted (saved for the first time) or
  *  deleted. This results in a more vigorous clearing of caches. In
  *  particular, when no flags exist yet, no Field admin UI paths exist and these

+ 3 - 3
sites/all/modules/contrib/flag/flag/includes/flag.export.inc

@@ -8,9 +8,9 @@
 /**
  * Export a flag to code.
  *
- * @param $flags
+ * @param array $flags
  *   An array of flag objects, or flag name.
- * @param $module
+ * @param string $module
  *   Optional. The name of the module that will be created if exporting to use
  *   in hook_flag_default_flags().
  */
@@ -247,7 +247,7 @@ function flag_update_page($flag) {
 /**
  * Update a flag before export.
  *
- * @param $flag
+ * @param flag_flag $flag
  *   The flag object passed by reference.
  */
 function flag_update_export(&$flag) {

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag.features.inc

@@ -71,7 +71,7 @@ function flag_features_export_render($module, $data) {
 /**
  * Implements hook_features_revert().
  *
- * @param $module
+ * @param string $module
  *   The name of module for which to revert content.
  */
 function flag_features_revert($module = NULL) {

+ 3 - 3
sites/all/modules/contrib/flag/flag/includes/flag.pages.inc

@@ -75,11 +75,11 @@ function flag_page($action, $flag, $entity_id) {
 /**
  * Form for confirming the (un)flagging of an entity.
  *
- * @param $action
+ * @param string $action
  *   Either 'flag' or 'unflag'.
- * @param $flag
+ * @param flag_flag $flag
  *   A loaded flag object.
- * @param $entity_id
+ * @param int $entity_id
  *   The id of the entity to operate on. The type is implicit in the flag.
  *
  * @see flag_confirm_submit()

+ 80 - 0
sites/all/modules/contrib/flag/flag/includes/flag/flag_comment.inc

@@ -69,6 +69,86 @@ class flag_comment extends flag_entity {
     return $comment->cid;
   }
 
+  /**
+   * Overrides flag_flag::get_flagging_record().
+   *
+   * This queries for flagging records for all comments on the node for the
+   * current comment, and prefills the flag_get_user_flags() static cache with
+   * the result for a performance gain.
+   */
+  function get_flagging_record($entity_id, $uid = NULL, $sid = NULL) {
+    static $seen_comment_nids = array();
+
+    $comment = $this->fetch_entity($entity_id);
+
+    // Figure out if this is the first comment we've seen for its parent node.
+    if (!isset($seen_comment_nids[$comment->nid])) {
+      // Preload the flag_get_user_flags() static cache with flagging records
+      // for all the comments on the node. This means that if multiple comments
+      // on this node are being viewed, only one query is run for all their
+      // flagging records, rather than one for each comment. This is because
+      // flag_get_user_flags() can only optimized across flags on one entity.
+      $flag_get_user_flags_cache = &drupal_static('flag_get_user_flags');
+
+      // We need to get a row for each comment, including empty ones if there is
+      // no flagging, so that the cache is warmed up for all comments. Therefore
+      // the query has to have the {comment} table as its base, and include the
+      // comment cid field.
+      $query = db_select('comment', 'c');
+      $query->leftJoin('flagging', 'f',
+        "c.cid = f.entity_id AND f.entity_type = 'comment'");
+      $query->fields('f')
+        ->fields('c', array('cid'))
+        ->condition('c.nid', $comment->nid)
+        ->condition(db_or()
+          // We want to include rows for comments which have no flaggings at all
+          // for the current user.
+          ->isNull('f.flagging_id')
+          // The same conditions as flag_get_user_flags()'s query.
+          ->condition(db_and()
+            ->condition(db_or()
+              ->condition('f.uid', $uid)
+              ->condition('f.uid', 0)
+            )
+            ->condition('f.sid', $sid)
+          )
+        );
+
+      // The result set can have multiple rows for a single comment, and rows
+      // which have no flagging ID, so there's nothing useful to index it by.
+      $result = $query->execute()->fetchAll();
+
+      $flag_names = _flag_get_flag_names();
+      foreach ($result as $flagging_data) {
+        $cid = $flagging_data->cid;
+
+        // At the very least, we need an empty array for the entity ID key in
+        // the cache array, so it counts as present.
+        if (!isset($flag_get_user_flags_cache[$uid][$sid]['comment'][$cid])) {
+          $flag_get_user_flags_cache[$uid][$sid]['comment'][$cid] = array();
+        }
+
+        // If the flagging table gave us no data, we're done with this row.
+        if (is_null($flagging_data->flagging_id)) {
+          continue;
+        }
+
+        // Remove the comment ID field from the row, so it's just the flagging
+        // table row.
+        unset($flagging_data->cid);
+
+        $flag_get_user_flags_cache[$uid][$sid]['comment'][$cid][$flag_names[$flagging_data->fid]] = $flagging_data;
+      }
+
+      // Mark that we've seen this node so we don't process it again.
+      $seen_comment_nids[$comment->nid] = TRUE;
+    }
+
+    // Return data for the comment we were asked about.
+    $user_flags = flag_get_user_flags($this->entity_type, $entity_id, $uid, $sid);
+    return isset($user_flags[$this->name]) ? $user_flags[$this->name] : NULL;
+  }
+
   function get_labels_token_types() {
     return array_merge(array('comment', 'node'), parent::get_labels_token_types());
   }

+ 59 - 45
sites/all/modules/contrib/flag/flag/includes/flag/flag_flag.inc

@@ -370,9 +370,9 @@ class flag_flag {
    * an object that hasn't yet been saved to the database. Subsequent calls to
    * fetch_entity() return the remembered object.
    *
-   * @param $entity_id
+   * @param int $entity_id
    *  The ID of the object to cache.
-   * @param $object
+   * @param stdClass $object
    *  The object to cache.
    */
   function remember_entity($entity_id, $object) {
@@ -430,13 +430,13 @@ class flag_flag {
   /**
    * Determines whether the user has the permission to use this flag.
    *
-   * @param $action
+   * @param string $action
    *   (optional) The action to test, either "flag" or "unflag". If none given,
    *   "flag" will be tested, which is the minimum permission to use a flag.
    * @param $account
    *   (optional) The user object. If none given, the current user will be used.
    *
-   * @return
+   * @return bool
    *   Boolean TRUE if the user is allowed to flag/unflag. FALSE otherwise.
    *
    * @see flag_permission()
@@ -461,16 +461,16 @@ class flag_flag {
    * This method typically should not be overridden by child classes. Instead
    * they should implement type_access(), which is called by this method.
    *
-   * @param $entity_id
+   * @param int $entity_id
    *   The entity ID to flag/unflag.
-   * @param $action
+   * @param string|NULL $action
    *   The action to test. Either 'flag' or 'unflag'. Leave NULL to determine
    *   by flag status.
-   * @param $account
+   * @param stdClass $account
    *   The user on whose behalf to test the flagging action. Leave NULL for the
    *   current user.
    *
-   * @return
+   * @return bool
    *   Boolean TRUE if the user is allowed to flag/unflag the given entity.
    *   FALSE otherwise.
    */
@@ -530,14 +530,14 @@ class flag_flag {
    * they should implement type_access_multiple(), which is called by this
    * method.
    *
-   * @param $entity_ids
+   * @param array $entity_ids
    *   The array of entity IDs to check. The keys are the entity IDs, the
    *   values are the actions to test: either 'flag' or 'unflag'.
-   * @param $account
+   * @param stdClass $account
    *   (optional) The account for which the actions will be compared against.
    *   If left empty, the current user will be used.
    *
-   * @return
+   * @return array
    *   An array whose keys are the object IDs and values are booleans indicating
    *   access.
    *
@@ -621,9 +621,9 @@ class flag_flag {
    * Utility function: Checks whether a flag applies to a certain type, and
    * possibly subtype, of entity.
    *
-   * @param $entity_type
+   * @param string $entity_type
    *   The type of entity being checked, such as "node".
-   * @param $content_subtype
+   * @param string|NULL $content_subtype
    *   The subtype being checked. For entities this will be the bundle name (the
    *   node type in the case of nodes).
    *
@@ -671,13 +671,13 @@ class flag_flag {
   /**
    * Flags, or unflags, an item.
    *
-   * @param $action
+   * @param string $action
    *   Either 'flag' or 'unflag'.
-   * @param $entity_id
+   * @param int $entity_id
    *   The ID of the item to flag or unflag.
-   * @param $account
+   * @param string|NULL $account
    *   The user on whose behalf to flag. Leave empty for the current user.
-   * @param $skip_permission_check
+   * @param bool $skip_permission_check
    *   Flag the item even if the $account user don't have permission to do so.
    * @param $flagging
    *   (optional) This method works in tandem with Drupal's Field subsystem.
@@ -695,7 +695,7 @@ class flag_flag {
    *  to take care of Field API validation, using either
    *  field_attach_form_validate() or field_attach_validate().
    *
-   * @return
+   * @return bool
    *   FALSE if some error occured (e.g., user has no permission, flag isn't
    *   applicable to the item, etc.), TRUE otherwise.
    */
@@ -769,22 +769,31 @@ class flag_flag {
       }
     }
 
-    // Perform the flagging or unflagging of this flag.
-    if ($action == 'unflag') {
-      if ($flagged) {
-        $this->flagging_delete($flagging, $entity_id, $account);
-      }
-      // We do nothing in the case of an attempt to unflag something that isn't
-      // actually flagged.
-    }
-    elseif ($action == 'flag') {
-      if (!$flagged) {
-        $this->flagging_insert($flagging, $entity_id, $account);
+    // Perform the flagging or unflagging of this flag
+    // along with a transaction mechanism.
+    $transaction = db_transaction();
+    try {
+      if ($action == 'unflag') {
+        if ($flagged) {
+          $this->flagging_delete($flagging, $entity_id, $account);
+        }
+        // We do nothing in the case of an attempt to unflag something that isn't
+        // actually flagged.
       }
-      else {
-        $this->flagging_update($flagging, $entity_id, $account);
+      elseif ($action == 'flag') {
+        if (!$flagged) {
+          $this->flagging_insert($flagging, $entity_id, $account);
+        }
+        else {
+          $this->flagging_update($flagging, $entity_id, $account);
+        }
       }
     }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception('flag', $e);
+      throw $e;
+    }
 
     return TRUE;
   }
@@ -980,11 +989,11 @@ class flag_flag {
    * Thanks to using a cache, inquiring several different flags about the same
    * item results in only one SQL query.
    *
-   * @param $uid
+   * @param int $uid
    *   (optional) The user ID whose flags we're checking. If none given, the
    *   current user will be used.
    *
-   * @return
+   * @return bool
    *   TRUE if the object is flagged, FALSE otherwise.
    */
   function is_flagged($entity_id, $uid = NULL, $sid = NULL) {
@@ -1008,7 +1017,10 @@ class flag_flag {
     $uid = $this->global ? 0 : (!isset($uid) ? $GLOBALS['user']->uid : $uid);
     $sid = $this->global ? 0 : (!isset($sid) ? flag_get_sid($uid) : $sid);
 
-    // flag_get_user_flags() does caching.
+    // Get all the flaggings for this user on the entity from
+    // flag_get_user_flags(), which will statically cache them. This means that
+    // when this method is called multiple times for all the flags on an entity,
+    // only the first call incurs a database query.
     $user_flags = flag_get_user_flags($this->entity_type, $entity_id, $uid, $sid);
     return isset($user_flags[$this->name]) ? $user_flags[$this->name] : NULL;
   }
@@ -1052,9 +1064,9 @@ class flag_flag {
   /**
    * Increases the flag count for an object and clears the static counts cache.
    *
-   * @param $entity_id
+   * @param int $entity_id
    *   For which item should the count be increased.
-   * @param $number
+   * @param int $number
    *   The amount of counts to increasing. Defaults to 1.
    *
    * @private
@@ -1085,9 +1097,9 @@ class flag_flag {
   /**
    * Decreases the flag count for an object and clears the static counts cache.
    *
-   * @param $entity_id
+   * @param int $entity_id
    *   For which item should the count be descreased.
-   * @param $number
+   * @param int $number
    *   The amount of counts to decrease. Defaults to 1.
    *
    * @private
@@ -1174,13 +1186,13 @@ class flag_flag {
    * E.g., do `print $flag->get_label('title')` instead of `print
    * $flag->title`.
    *
-   * @param $label
+   * @param string $label
    *   The label to get, e.g. 'title', 'flag_short', 'unflag_short', etc.
-   * @param $entity_id
+   * @param int $entity_id
    *   The ID in whose context to interpret tokens. If not given, only global
    *   tokens will be substituted.
    *
-   * @return
+   * @return string
    *   The processed label.
    */
   function get_label($label, $entity_id = NULL) {
@@ -1408,6 +1420,8 @@ class flag_flag {
    * Deletes a flag from the database.
    */
   function delete() {
+    field_attach_delete_bundle('flagging', $this->name);
+
     db_delete('flag')->condition('fid', $this->fid)->execute();
     db_delete('flagging')->condition('fid', $this->fid)->execute();
     db_delete('flag_types')->condition('fid', $this->fid)->execute();
@@ -1520,18 +1534,18 @@ class flag_flag {
    * This is a wrapper around theme('flag') that channels the call to the right
    * template file.
    *
-   * @param $action
+   * @param string $action
    *  The action the link is about to carry out, either "flag" or "unflag".
-   * @param $entity_id
+   * @param int $entity_id
    *  The ID of the object to flag.
-   * @param $variables = array()
+   * @param array $variables
    *  An array of further variables to pass to theme('flag'). For the full list
    *  of parameters, see flag.tpl.php. Of particular interest:
    *  - after_flagging: Set to TRUE if this flag link is being displayed as the
    *    result of a flagging action.
    *  - errors: An array of error messages.
    *
-   * @return
+   * @return string
    *  The HTML for the flag link.
    */
   function theme($action, $entity_id, $variables = array()) {

+ 2 - 2
sites/all/modules/contrib/flag/flag/includes/flag/flag_node.inc

@@ -87,10 +87,10 @@ class flag_node extends flag_entity {
   /**
    * Adjust the Content ID to find the translation parent if i18n-enabled.
    *
-   * @param $entity_id
+   * @param int $entity_id
    *   The nid for the content.
    *
-   * @return
+   * @return int
    *   The tnid if available, the nid otherwise.
    */
   function get_translation_id($entity_id) {

+ 21 - 0
sites/all/modules/contrib/flag/flag/includes/views/flag.views.inc

@@ -140,6 +140,27 @@ function flag_views_data() {
     ),
   );
 
+  $data['flagging']['flagging_id'] = array(
+    'title' => t('Flagging ID'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+      'help' => t('Display the flagging ID.'),
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+      'help' => t('Sort by the flagging ID.'),
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+      'help' => t('Filter by the flagging ID.'),
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_numeric',
+      'help' => t('The unique ID of the flagging.'),
+    ),
+  );
+
   $data['flag_counts']['count'] = array(
     'title' => t('Flag counter'),
     'help' => t('The number of times a piece of content is flagged by any user.'),

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_argument_entity_id.inc

@@ -40,7 +40,7 @@ class flag_handler_argument_entity_id extends views_handler_argument_numeric {
       ->condition('o.' . $views_info['join field'], $this->value, 'IN')
       ->execute();
     foreach ($result as $title) {
-      $titles[] = check_plain($title->$views_info['title field']);
+      $titles[] = check_plain($title->{$views_info['title field']});
     }
     return $titles;
   }

+ 128 - 2
sites/all/modules/contrib/flag/flag/tests/flag.test

@@ -13,10 +13,10 @@ abstract class FlagTestCaseBase extends DrupalWebTestCase {
   /**
    * Helper to create a flag from an array of data and clear caches etc.
    *
-   * @param $flag_data
+   * @param array $flag_data
    *  An array of flag data.
    *
-   * @return
+   * @return flag_flag
    *  The flag object.
    */
   function createFlag($flag_data) {
@@ -1656,3 +1656,129 @@ class FlagHookInvocationsTestCase extends FlagTestCaseBase {
   }
 
 }
+
+/**
+ * Verifies the optimization for comment viewing.
+ */
+class FlagCommentOptimizationTestCase extends FlagTestCaseBase {
+
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Comment view optimization',
+      'description' => 'Tests that loading of flagging records when viewing all comments on a node works correctly.',
+      'group' => 'Flag',
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp(array(
+      'flag',
+      'comment',
+      'flag_comment_flag_test',
+    ));
+
+    $flag_data = array(
+      'entity_type' => 'comment',
+      'name' => 'test_flag',
+      'title' => 'Test Flag',
+      'global' => 0,
+      'types' => array(),
+      'flag_short' => 'Flag this item',
+      'flag_long' => '',
+      'flag_message' => '',
+      'unflag_short' => 'Unflag this item',
+      'unflag_long' => '',
+      'unflag_message' => '',
+      'unflag_denied_text' => 'You may not unflag this item',
+      'link_type' => 'normal',
+      'weight' => 0,
+      'show_on_form' => 0,
+      'access_author' => '',
+      'show_contextual_link' => 0,
+      'show_in_links' => array(
+        'full' => 1,
+        'teaser' => 1,
+      ),
+      'i18n' => 0,
+      'api_version' => 3,
+    );
+    $this->flag = $this->createFlag($flag_data);
+
+    // Set up comments on article nodes.
+    variable_set('comment_form_location_article', COMMENT_FORM_BELOW);
+
+    // Create test user who can flag and unflag.
+    $this->user = $this->drupalCreateUser(array(
+      'access comments',
+      'post comments',
+      'flag test_flag',
+      'unflag test_flag',
+    ));
+    $this->drupalLogin($this->user);
+
+    // Create an article node.
+    $title = $this->randomName(8);
+    $node = array(
+      'title' => $title,
+      'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(32)))),
+      'uid' => $this->user->uid,
+      'type' => 'article',
+      'is_new' => TRUE,
+      'comment' => COMMENT_NODE_OPEN,
+    );
+    $node = node_submit((object) $node);
+    node_save($node);
+
+    $this->nid = $node->nid;
+
+    // Create some comments on the node.
+    $this->cids = array();
+    foreach (range(1, 10) as $i) {
+      $comment = (object) array(
+        'cid' => NULL,
+        'nid' => $node->nid,
+        'pid' => 0,
+        'uid' => $this->user->uid,
+        'status' => COMMENT_PUBLISHED,
+        'subject' => $this->randomName(),
+        'language' => LANGUAGE_NONE,
+        'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
+      );
+      comment_save($comment);
+
+      $this->cids[$comment->cid] = $comment->cid;
+    }
+
+    // Flag one comment.
+    $this->flag->flag('flag', reset($this->cids));
+  }
+
+  /**
+   * Test viewing multiple comments on a node is optimized.
+   */
+  function testCommentView() {
+    // View the node.
+    $this->drupalGet('node/' . $this->nid);
+
+    // Inspect the tracking variable.
+    // Hooks in the flag_comment_flag_test module will have populated this with
+    // data at various points in the lifecycle of the loaded page.
+    $tracking_variable = variable_get('flag_comment_flag_test_user_flags_cache_tracking', array());
+
+    $this->assertNull($tracking_variable['hook_comment_load'], "The flag_get_user_flags() static cache is empty when the comments are being loaded.");
+
+    // The test module's hook_entity_view() runs after flag's implementation, so
+    // for the first comment view, the cache should be fully populated: all
+    // comments should have an entry.
+    foreach ($this->cids as $cid) {
+      $this->assertNotNull($tracking_variable['hook_entity_view_1'][$this->user->uid][0]['comment'][$cid], "The static cache contained data for comment $cid when comment 1 was being viewed.");
+    }
+  }
+
+}

+ 13 - 0
sites/all/modules/contrib/flag/flag/tests/flag_comment_flag_test/flag_comment_flag_test.info

@@ -0,0 +1,13 @@
+name = Flag Comment Flag Test
+description = Test module for comment flags.
+dependencies[] = flag
+dependencies[] = comment
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
+core = "7.x"
+project = "flag"
+datestamp = "1474619065"
+

+ 41 - 0
sites/all/modules/contrib/flag/flag/tests/flag_comment_flag_test/flag_comment_flag_test.module

@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file flag_comment_flag_test.module
+ * Test module for comment flags.
+ */
+
+/**
+ * Implements hook_comment_load().
+ *
+ * This is only called once when viewing a node with comments, and before
+ * hook_entity_view() is invoked. We use this to check the initial state of the
+ * cache.
+ */
+function flag_comment_flag_test_comment_load($comments) {
+  $flag_get_user_flags_cache = drupal_static('flag_get_user_flags');
+
+  // Store the value of the flag_get_user_flags() static cache in the variable,
+  // so the test can retrieve it.
+  $tracking_variable = variable_get('flag_comment_flag_test_user_flags_cache_tracking', array());
+  $tracking_variable['hook_comment_load'] = $flag_get_user_flags_cache;
+  variable_set('flag_comment_flag_test_user_flags_cache_tracking', $tracking_variable);
+}
+
+/**
+ * Implements hook_entity_view().
+ *
+ * Use hook_entity_view() rather than hook_comment_view() so we are in the same
+ * invocation as flag_entity_view().
+ */
+function flag_comment_flag_test_entity_view($entity, $type, $view_mode, $langcode) {
+  if ($type == 'comment') {
+    $flag_get_user_flags_cache = drupal_static('flag_get_user_flags');
+
+    // Store the value of the flag_get_user_flags() static cache in the variable,
+    // so the test can retrieve it.
+    $tracking_variable = variable_get('flag_comment_flag_test_user_flags_cache_tracking', array());
+    $tracking_variable['hook_entity_view_' . $entity->cid] = $flag_get_user_flags_cache;
+    variable_set('flag_comment_flag_test_user_flags_cache_tracking', $tracking_variable);
+  }
+}

+ 3 - 3
sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.info

@@ -4,9 +4,9 @@ dependencies[] = flag
 core = 7.x
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 3 - 3
sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.info

@@ -5,9 +5,9 @@ dependencies[] = rules
 core = 7.x
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 2 - 2
sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.module

@@ -10,9 +10,9 @@
  *
  * Hook implementations should call this with their hook name and parameters.
  *
- * @param $hook_name
+ * @param string $hook_name
  *  The name of the hook invoked.
- * @param $function_parameters
+ * @param array $function_parameters
  *  The array of parameters the hook received.
  * @param $flagging
  *  (optional) The flagging entity that the hook received. If this is given,

+ 3 - 3
sites/all/modules/contrib/flag/flag/tests/flagaccesstest/flagaccesstest.info

@@ -5,9 +5,9 @@ dependencies[] = flag
 package = Flags
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2015-03-02
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-09-23
+version = "7.x-3.9"
 core = "7.x"
 project = "flag"
-datestamp = "1425327793"
+datestamp = "1474619065"
 

+ 18 - 23
sites/all/modules/contrib/form/webform/includes/webform.submissions.inc

@@ -664,14 +664,24 @@ function webform_get_submissions($filters = array(), $header = NULL, $pager_coun
   }
 
   // Query the required submission data.
-  $query = db_select('webform_submitted_data', 'sd');
-  $query->leftJoin('webform_submissions', 's', 's.sid = sd.sid');
+  $query = db_select('webform_submissions', 's');
   $query->leftJoin('users', 'u', 'u.uid = s.uid');
   $query
     ->fields('s')
-    ->fields('sd', array('cid', 'no', 'data'))
     ->fields('u', array('name'))
-    ->condition('sd.sid', $sids, 'IN')
+    ->condition('s.sid', $sids, 'IN');
+
+  $submissions = $query->execute()->fetchAllAssoc('sid');
+
+  foreach ($submissions as $sid => $submission) {
+    $submissions[$sid]->data = array();
+  }
+
+  // Query the required submission data.
+  $query = db_select('webform_submitted_data', 'sd');
+  $query
+    ->fields('sd', array('sid', 'cid', 'no', 'data'))
+    ->condition('sd.sid', array_keys($submissions), 'IN')
     ->orderBy('sd.sid', 'ASC')
     ->orderBy('sd.cid', 'ASC')
     ->orderBy('sd.no', 'ASC');
@@ -682,27 +692,12 @@ function webform_get_submissions($filters = array(), $header = NULL, $pager_coun
     $query->condition('sd.nid', $filters['nid']);
   }
 
-  $result = $query->execute();
-
-  // Convert the queried rows into submissions.
-  $previous = 0;
-  foreach ($result as $row) {
-    if ($row->sid != $previous) {
-      $submissions[$row->sid] = new stdClass();
-      $submissions[$row->sid]->sid = $row->sid;
-      $submissions[$row->sid]->nid = $row->nid;
-      $submissions[$row->sid]->submitted = $row->submitted;
-      $submissions[$row->sid]->remote_addr = $row->remote_addr;
-      $submissions[$row->sid]->uid = $row->uid;
-      $submissions[$row->sid]->name = $row->name;
-      $submissions[$row->sid]->is_draft = $row->is_draft;
-      $submissions[$row->sid]->data = array();
-    }
-    // CID may be NULL if this submission does not actually contain any data.
-    if ($row->cid) {
+  if ($submissions) {
+    $result = $query->execute();
+    // Convert the queried rows into submission data.
+    foreach ($result as $row) {
       $submissions[$row->sid]->data[$row->cid]['value'][$row->no] = $row->data;
     }
-    $previous = $row->sid;
   }
 
   foreach (module_implements('webform_submission_load') as $module) {

+ 3 - 3
sites/all/modules/contrib/form/webform/webform.info

@@ -24,9 +24,9 @@ files[] = tests/permissions.test
 files[] = tests/submission.test
 files[] = tests/webform.test
 
-; Information added by Drupal.org packaging script on 2015-04-02
-version = "7.x-3.24"
+; Information added by Drupal.org packaging script on 2016-10-19
+version = "7.x-3.25"
 core = "7.x"
 project = "webform"
-datestamp = "1427956663"
+datestamp = "1476870845"
 

+ 15 - 1
sites/all/modules/contrib/form/webform/webform.module

@@ -1042,17 +1042,31 @@ function webform_file_download($uri) {
     $submission = reset($submissions);
   }
 
-  // Grant access based on access to the submission.
+  // Grant or deny file access based on access to the submission.
   if (!empty($submission)) {
     $node = node_load($submission->nid);
     if (webform_submission_access($node, $submission)) {
       return file_get_content_headers($file);
     }
+    else {
+      return -1;
+    }
   }
   // Grant access to files uploaded by a user before the submission is saved.
   elseif (!empty($file) && !empty($_SESSION['webform_files'][$file->fid])) {
     return file_get_content_headers($file);
   }
+
+  // Ensure we never completely ignore a webform file request.
+  if (strpos(file_uri_target($uri), 'webform/') === 0) {
+    // The file is not part of a submission or a submission-in-progress (by
+    // the current user), however it may be part of a submission-in-progress
+    // (or an abandoned submission) by another user. We assume that all files
+    // under our enforced directory prefix are in fact webform files, and so
+    // we deny access to the file. Abandoned uploads will be deleted by
+    // system_cron() in due course.
+    return -1;
+  }
 }
 
 /**

+ 2 - 5
sites/all/modules/contrib/panels/panels/README.txt

@@ -1,6 +1,3 @@
+Welcome to Panels 3
 
-Welcome to Panels 3.
-
-A little documentation should go here, but Panels 3 is alsoi a beast - you're
-best off checking the online handbook on Drupal.org, or this issue:
-http://drupal.org/node/887560. 
+Documentation is available at https://www.drupal.org/node/496278

+ 11 - 1
sites/all/modules/contrib/panels/panels/UPGRADE.txt

@@ -11,7 +11,7 @@ Upgrading from Panels-6.x-3.x to Panels-7.x-3.x
 
   - panels_plugin_get_function() deprecated.
 
-  - panels_required_context removed. These were deprecated long ago and 
+  - panels_required_context removed. These were deprecated long ago and
     existed only to prevent crashes.
 
   - panels_optional_context removed.
@@ -20,3 +20,13 @@ Upgrading from Panels-6.x-3.x to Panels-7.x-3.x
 
   - display_renderer class is now in 'renderer', not 'handler'.
 
+Upgrading task handlers from Panels 7.x-3.5 or older to Panels 7.x-3.6 and newer:
+
+  - You must specify a storage type for any panels display using your custom task handler.
+    For examples, see panels_update_7306.
+
+  - When creating whatever stores the panel, a storage id and storage type must be defined.
+    See panels_mini.module for examples inside panels_mini_save and panels_mini_panels_cache_get.
+
+  - A display access plugin must be defined.
+    See panels_mini/plugins/panels_storage/panels_mini.inc for an example plugin.

+ 0 - 5
sites/all/modules/contrib/panels/panels/css/panels.css

@@ -40,11 +40,6 @@ div.panel-pane:hover div.panel-hide {
   margin-top: -1.5em;
 }
 
-div.panel-pane div.node {
-  margin: 0;
-  padding: 0;
-}
-
 div.panel-pane div.feed a {
   float: right;
 }

+ 14 - 2
sites/all/modules/contrib/panels/panels/css/panels_dnd.css

@@ -305,11 +305,19 @@ a.close img {
   background: url(../images/bg-content-modal.png);
   height: 100%;
   margin: -1em;
-  padding-top: 1em;
+  padding-bottom: 1em;
   padding-left: 175px;
   position: relative;
 }
 
+.panels-section-columns-quickfilter {
+  padding-top: 1em;
+  padding-left: 1em;
+  padding-bottom: 1em;
+  margin-bottom: 1em;
+  background-color: #EEEEEE;
+}
+
 .panels-section-columns {
   height: 100%;
   overflow: auto;
@@ -371,7 +379,11 @@ a.close img {
 
 .content-type-button a {
   display: inline-block;
-  width: 100%;
+  width: 99%;
+}
+
+.content-type-button a:focus {
+  border: 1px dotted black;
 }
 
 .content-type-button img {

+ 1 - 1
sites/all/modules/contrib/panels/panels/help/plugins-layout.html

@@ -48,7 +48,7 @@ The include file defines all the other files that our layout will utilize in ord
 <ol>
 <li><strong>Title:</strong><br />The title of our layout. (Utilized within the panels administration screens)</li>
 <li><strong>Icon:</strong><br />The graphical representation of our layout. (Utilized within the panels administration screens)</li>
-<li><strong>Theme:</strong><br />The template file of our layout. (Sharp eyed readers will note that the theme definition utilizes underscores instead of dashes, and does not have ".tpl.php" after it. This is refering to the layout-sample-first-layout.tpl.php file all the same, it is simply how the naming convention works. Utilize dashes in the tpl file name and underscores when refering to it in your include file.)</li>
+<li><strong>Theme:</strong><br />The template file of our layout. (Sharp eyed readers will note that the theme definition utilizes underscores instead of dashes, and does not have ".tpl.php" after it. This is referring to the layout-sample-first-layout.tpl.php file all the same, it is simply how the naming convention works. Utilize dashes in the tpl file name and underscores when referring to it in your include file.)</li>
 <li><strong>CSS:</strong><br />The css file to be utilized for our layout. (Utilized within the panels administration screens, AND when viewing the actual panel itself.)</li>
 <li><strong>Panels:</strong><br />Defines all the various regions within your panel. This will be further utilized within our tpl.php file.</li>
 </ol>

+ 3 - 3
sites/all/modules/contrib/panels/panels/i18n_panels/i18n_panels.info

@@ -7,9 +7,9 @@ dependencies[] = i18n_translation
 package = Multilingual - Internationalization
 core = 7.x
 
-; Information added by Drupal.org packaging script on 2015-01-28
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-3.8"
 core = "7.x"
 project = "panels"
-datestamp = "1422472985"
+datestamp = "1476582295"
 

+ 2 - 0
sites/all/modules/contrib/panels/panels/includes/add-content.inc

@@ -9,6 +9,8 @@
  * Preprocess the primary entry level theme.
  */
 function template_preprocess_panels_add_content_modal(&$vars) {
+  $vars['categories_array'] = array();
+
   // Process the list of categories.
   foreach ($vars['categories'] as $key => $category_info) {
     // 'root' category is actually displayed under the categories, so

+ 29 - 11
sites/all/modules/contrib/panels/panels/includes/common.inc

@@ -89,7 +89,7 @@ class panels_allowed_layouts {
    *  as allowed or not allowed on the initial call to panels_allowed_layouts::set_allowed()
    *
    */
-  function panels_allowed_layouts($start_allowed = TRUE) {
+  function __construct($start_allowed = TRUE) {
     // TODO would be nice if there was a way to just fetch the names easily
     foreach ($this->list_layouts() as $layout_name) {
       $this->allowed_layout_settings[$layout_name] = $start_allowed ? 1 : 0;
@@ -196,7 +196,7 @@ class panels_allowed_layouts {
    */
   function save() {
     if (!is_null($this->module_name)) {
-      variable_set($this->module_name . "_allowed_layouts", serialize($this));
+      variable_set($this->module_name . '_allowed_layouts', serialize($this));
     }
   }
 
@@ -272,7 +272,11 @@ function panels_common_settings($form, &$form_state, $module_name = 'panels_comm
     // each type its own checkboxes set unless it's 'single' in which
     // case it can go into our fake other set.
     $available_content_types = ctools_content_get_all_types();
-    $allowed_content_types = variable_get($module_name . '_allowed_types', array());
+    $allowed_content_types = db_select('panels_allowed_types', 'pat')
+      ->fields('pat', array('type', 'allowed'))
+      ->condition('module', $module_name)
+      ->execute()
+      ->fetchAllKeyed();
 
     foreach ($available_content_types as $id => $types) {
       foreach ($types as $type => $info) {
@@ -352,13 +356,23 @@ function panels_common_settings_submit($form, &$form_state) {
   $module_name = $form_state['values']['module_name'];
   variable_set($module_name . '_default', $form_state['values']['panels_common_default']);
   if (!$form_state['skip']) {
-    // merge the broken apart array neatly back together
-    $allowed = $form_state['values']['allowed'];
+    // Merge the broken apart array neatly back together.
     $allowed_content_types = array();
-    foreach ($form_state['values']['allowed'] as $allowed) {
-      $allowed_content_types = array_merge($allowed_content_types, $form_state['values']['content_types'][$allowed]['options']);
+    $content_types = $form_state['values']['allowed'];
+    foreach ($content_types as $content_type) {
+      $allowed_content_types = array_merge($allowed_content_types, $form_state['values']['content_types'][$content_type]['options']);
+      foreach ($allowed_content_types as $type => $allowed) {
+        $allowed = empty($allowed) ? 0 : 1;
+        db_merge('panels_allowed_types')
+          ->key(array('module' => $module_name, 'type' => $type))
+          ->fields(array(
+            'module' => $module_name,
+            'type' => $type,
+            'allowed' => $allowed,
+          ))
+          ->execute();
+      }
     }
-    variable_set($module_name . '_allowed_types', $allowed_content_types);
   }
   drupal_set_message(t('Your changes have been saved.'));
 }
@@ -368,9 +382,13 @@ function panels_common_settings_submit($form, &$form_state) {
  */
 function panels_common_get_allowed_types($module, $contexts = array(), $has_content = FALSE, $default_defaults = array(), $default_allowed_types = array()) {
   // Get a list of all types that are available
-
   $default_types = variable_get($module . '_default', $default_defaults);
-  $allowed_types = variable_get($module . '_allowed_types', $default_allowed_types);
+  $allowed_types = db_select('panels_allowed_types', 'pat')
+    ->fields('pat', array('type', 'allowed'))
+    ->condition('module', $module)
+    ->execute()
+    ->fetchAllKeyed();
+  $allowed_types = !empty($allowed_types) ? $allowed_types : $default_allowed_types;
 
   // By default, if they haven't gone and done the initial setup here,
   // let all 'other' types (which will be all types) be available.
@@ -455,7 +473,7 @@ function panels_common_allowed_layouts_form_submit($form, &$form_state) {
  * Get the allowed layout object for the given module.
  */
 function panels_common_get_allowed_layout_object($module_name) {
-  $allowed_layouts = unserialize(variable_get($module_name . "_allowed_layouts", serialize('')));
+  $allowed_layouts = unserialize(variable_get($module_name . '_allowed_layouts', serialize('')));
 
   // if no parameter was provided, or the variable_get failed
   if (!$allowed_layouts) {

+ 2 - 2
sites/all/modules/contrib/panels/panels/includes/display-edit.inc

@@ -302,7 +302,7 @@ function panels_edit_display_settings_form($form, &$form_state) {
  * Validate the layout settings form.
  */
 function panels_edit_display_settings_form_validate($form, &$form_state) {
-  if ($function = panels_plugin_get_function('layout', $form_state['layout'], 'settings validate')) {
+  if ($function = panels_plugin_get_function('layouts', $form_state['layout'], 'settings validate')) {
     $function($form_state['values']['layout_settings'], $form['layout_settings'], $form_state['display'], $form_state['layout'], $form_state['display']->layout_settings);
   }
 }
@@ -312,7 +312,7 @@ function panels_edit_display_settings_form_validate($form, &$form_state) {
  */
 function panels_edit_display_settings_form_submit($form, &$form_state) {
   $display = &$form_state['display'];
-  if ($function = panels_plugin_get_function('layout', $form_state['layout'], 'settings submit')) {
+  if ($function = panels_plugin_get_function('layouts', $form_state['layout'], 'settings submit')) {
     $function($form_state['values']['layout_settings'], $display, $form_state['layout'], $display->layout_settings);
   }
 

+ 2 - 0
sites/all/modules/contrib/panels/panels/includes/display-layout.inc

@@ -321,6 +321,8 @@ function panels_change_layout_submit($form, &$form_state) {
   $display->panels = $content;
 
   $display->layout = $form_state['layout'];
+
+  panels_edit_display_settings_form_submit($form, $form_state);
 }
 
 /**

+ 26 - 1
sites/all/modules/contrib/panels/panels/includes/plugins.inc

@@ -125,7 +125,7 @@ class panels_cache_object {
   /**
    * When constructed, take a snapshot of our existing out of band data.
    */
-  function panels_cache_object() {
+  function __construct() {
     $this->head = drupal_add_html_head();
     $this->css = drupal_add_css();
     $this->tokens = ctools_set_page_token();
@@ -478,6 +478,31 @@ function panels_get_renderer_pipelines($sort = TRUE) {
   return $pipelines;
 }
 
+/**
+ * Fetch metadata on a specific panels_storage plugin.
+ *
+ * @param $storage
+ *   Name of a panel_storage plugin.
+ *
+ * @return
+ *   An array with information about the requested panels_storage plugin
+ */
+function panels_get_panels_storage_plugin($storage) {
+  ctools_include('plugins');
+  return ctools_get_plugins('panels', 'panels_storage', $storage);
+}
+
+/**
+ * Fetch metadata for all panels_storage plugins.
+ *
+ * @return
+ *   An array of arrays with information about all available panels_storage plugins.
+ */
+function panels_get_panels_storage_plugins() {
+  ctools_include('plugins');
+  return ctools_get_plugins('panels', 'panels_storage');
+}
+
 /**
  * Get a function from a plugin, if it exists.
  *

+ 39 - 0
sites/all/modules/contrib/panels/panels/js/panels-base.js

@@ -13,6 +13,45 @@
     }
   };
 
+  Drupal.Panels.AddContentModalQuickFilter = function() {
+    var input_field = $('.panels-add-content-modal input[name=quickfilter]');
+    input_field.data.panelsAddContentModalQuickFilter = {
+      keyupTimeout: false,
+      filter: function(e) {
+        if (this.val().length) {
+          var search_expression = this.val().toLowerCase();
+          $('.panels-add-content-modal .panels-section-columns .content-type-button').each(function(i, elem) {
+            if ($(elem).text().toLowerCase().search(search_expression) > -1) {
+              $(elem).show();
+            }
+            else {
+              $(elem).hide();
+            }
+          });
+        }
+        else {
+          $('.panels-add-content-modal .panels-section-columns .content-type-button').show();
+        }
+      }
+    }
+    // Use timeout to reduce the iteration over the DOM tree.
+    input_field.bind('keyup.AddContentModalQuickFilter', jQuery.proxy(function(e){
+      var filter = $(this).data.panelsAddContentModalQuickFilter;
+      if (filter.keyupTimeout) {
+        window.clearTimeout(filter.timeout);
+        filter.keyupTimeout = false;
+      }
+      // If there's only one item left and enter is hit select it right away.
+      if (e.keyCode == 13 && $('.panels-add-content-modal .panels-section-columns .content-type-button:visible').length == 1) {
+        $('.panels-add-content-modal .panels-section-columns .content-type-button:visible a').trigger('click');
+      }
+      else {
+        filter.keyupTimeout = window.setTimeout(jQuery.proxy(filter.filter, this), 200);
+      }
+    }, input_field));
+    input_field.focus();
+  };
+
   Drupal.Panels.restripeTable = function(table) {
     // :even and :odd are reversed because jquery counts from 0 and
     // we count from 1, so we're out of sync.

+ 12 - 5
sites/all/modules/contrib/panels/panels/panels.info

@@ -2,18 +2,25 @@ name = Panels
 description = Core Panels display functions; provides no external UI, at least one other Panels module should be enabled.
 core = 7.x
 package = "Panels"
-version = PANELS_VERSION
 configure = admin/structure/panels
-dependencies[] = ctools (>1.5)
+dependencies[] = ctools
 files[] = panels.module
 files[] = includes/common.inc
 files[] = includes/legacy.inc
 files[] = includes/plugins.inc
 files[] = plugins/views/panels_views_plugin_row_fields.inc
 
-; Information added by Drupal.org packaging script on 2015-01-28
-version = "7.x-3.5"
+; Tests.
+; Task handlers for entities. These inherit off one base class to make it easier
+; to repeat tests across multiple entity types.
+files[] = tests/PanelsEntityViewWebTestCase.test
+files[] = tests/PanelsNodeViewWebTestCase.test
+files[] = tests/PanelsTermViewWebTestCase.test
+files[] = tests/PanelsUserViewWebTestCase.test
+
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-3.8"
 core = "7.x"
 project = "panels"
-datestamp = "1422472985"
+datestamp = "1476582295"
 

+ 198 - 1
sites/all/modules/contrib/panels/panels/panels.install

@@ -47,7 +47,69 @@ function panels_requirements_install() {
 function panels_schema() {
   // This should always point to our 'current' schema. This makes it relatively
   // easy to keep a record of schema as we make changes to it.
-  return panels_schema_6();
+  return panels_schema_9();
+}
+
+function panels_schema_9() {
+  $schema = panels_schema_8();
+  $schema['panels_allowed_types'] = array(
+    'fields' => array(
+      'module' => array(
+        'description' => 'The name of the module requiring allowed type settings.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'type' => array(
+        'description' => 'Ctools content type to allow.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'allowed' => array(
+        'description' => 'A boolean for if the type is allowed or not.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'default' => 1,
+      ),
+    ),
+    'indexes' => array(
+      'type_idx' => array('type'),
+    ),
+  );
+
+  return $schema;
+ }
+
+function panels_schema_8() {
+  $schema = panels_schema_7();
+
+  // Add the storage type and id columns.
+  $schema['panels_display']['fields']['storage_type'] = array(
+    'type' => 'varchar',
+    'length' => 255,
+    'default' => '',
+  );
+  $schema['panels_display']['fields']['storage_id'] = array(
+    'type' => 'varchar',
+    'length' => 255,
+    'default' => '',
+  );
+
+  return $schema;
+}
+
+function panels_schema_7() {
+  $schema = panels_schema_6();
+
+  // Update field lengths to 255 chars.
+  $schema['panels_pane']['fields']['subtype']['length'] = '255';
+  $schema['panels_pane']['fields']['panel']['length'] = '255';
+  $schema['panels_pane']['fields']['type']['length'] = '255';
+
+  return $schema;
 }
 
 function panels_schema_6() {
@@ -406,11 +468,23 @@ function panels_update_7301() {
 
 /**
  * Adding universally unique identifiers to panels.
+ *
+ * Note: This update hook is not written well. It calls apis which uses the
+ * most updated drupal database, causing missing columns or tables errors. To
+ * mitigate the issue, we've added updates from 7303 and 7305. Future updates
+ * should go below the 7305 update.
+ *
+ * See https://www.drupal.org/node/2787123 for more info.
  */
 function panels_update_7302() {
   if (!module_load_include('inc', 'ctools', 'includes/uuid')) {
     throw new DrupalUpdateException(t('Ctools UUID support not detected. You must update to a more recent version of the ctools module.'));
   }
+  // Run the 7303 update first to avoid caching issues.
+  // This *probably* should be placed right above update 7305, however it was
+  // tested here and re-testing this update is difficult, so it stays here.
+  panels_update_7303();
+
   // Load the schema.
   $schema = panels_schema_5();
   $msg = array();
@@ -462,6 +536,10 @@ function panels_update_7302() {
 
   $dids = array_unique(array_merge($display_dids, $pane_dids));
 
+  // Before using panels_save_display(), we have to make sure any new fields
+  // are added from future updates.
+  panels_update_7305();
+
   // If the Panels module is disabled we don't have access to
   // panels_load_displays().
   if (!function_exists('panels_load_displays')) {
@@ -492,3 +570,122 @@ function panels_update_7303() {
     db_create_table($table_name, $schema[$table_name]);
   }
 }
+
+/**
+ * Update "panels_pane" table field lengths to 255 chars.
+ */
+function panels_update_7304() {
+  $schema = panels_schema_7();
+
+  $update_fields = array(
+    'panels_pane' => array('subtype', 'panel', 'type'),
+  );
+
+  foreach ($update_fields as $table => $fields) {
+    foreach ($fields as $field_name) {
+      db_change_field($table, $field_name, $field_name, $schema[$table]['fields'][$field_name]);
+    }
+  }
+}
+
+/**
+ * Add the "storage_type" and "storage_id" columns to "panels_display".
+ */
+function panels_update_7305() {
+  $schema = panels_schema_8();
+
+  $new_fields = array(
+    'panels_display' => array('storage_type', 'storage_id'),
+  );
+
+  foreach ($new_fields as $table => $fields) {
+    foreach ($fields as $field_name) {
+      // Due to a previous failure, the column may already exist:
+      if (!db_field_exists($table, $field_name)) {
+        db_add_field($table, $field_name, $schema[$table]['fields'][$field_name]);
+      }
+    }
+  }
+}
+
+/**
+ * Set the storage type and id on existing page manager panels displays.
+ */
+function panels_update_7306() {
+  if (!db_table_exists('page_manager_handlers')) {
+    return t('Skipping update - page_manager is not installed.');
+  }
+
+  // Get all page_manager_handlers that have a panels context.
+  $result = db_query("SELECT pm.name, pm.conf FROM {page_manager_handlers} pm WHERE pm.handler = 'panel_context'");
+  $page_manager_panels = array();
+  foreach ($result as $row) {
+    $conf = unserialize($row->conf);
+    if (isset($conf['did'])) {
+      $page_manager_panels[$conf['did']] = $row->name;
+    }
+  }
+
+  if (!empty($page_manager_panels)) {
+    // Check panels displays that only have empty storage types
+    $result = db_query("SELECT pd.did FROM {panels_display} pd WHERE pd.did IN (:dids) AND storage_type = ''", array(':dids' => array_keys($page_manager_panels)));
+    foreach ($result as $row) {
+      db_update('panels_display')
+        ->fields(array(
+          'storage_type' => 'page_manager',
+          'storage_id' => $page_manager_panels[$row->did],
+        ))
+        ->condition('did', $row->did)
+        ->execute();
+    }
+  }
+}
+
+/**
+ * Add a custom table for allowed types.
+ */
+function panels_update_7307() {
+  $schema = panels_schema_9();
+
+  $table_name = 'panels_allowed_types';
+  if (!db_table_exists($table_name)) {
+    db_create_table($table_name, $schema[$table_name]);
+  }
+
+  // Read existing allowed settings and store them in a new table.
+  $variables = db_select('variable', 'v')
+    ->fields('v', array('name'))
+    ->condition('name', '%' . db_like('_allowed_types'), 'LIKE')
+    ->execute()
+    ->fetchCol();
+  foreach ($variables as $name) {
+    $module = str_replace('_allowed_types', '', $name);
+    $variable = variable_get($name);
+    foreach ($variable as $type => $allowed) {
+      $allowed = empty($allowed) ? 0 : 1;
+      db_merge('panels_allowed_types')
+        ->key(array('module' => $module, 'type' => $type))
+        ->fields(array(
+          'module' => $module,
+          'type' => $type,
+          'allowed' => $allowed,
+        ))
+        ->execute();
+    }
+    variable_del($name);
+  }
+}
+
+/**
+ * Rename style permissions.
+ */
+function panels_update_7308() {
+  $permissions = array(
+    'administer panels display styles',
+    'administer panels pane styles',
+    'administer panels region styles',
+  );
+  foreach (array_keys(user_roles(TRUE, 'administer panels styles')) as $rid) {
+    user_role_grant_permissions($rid, $permissions);
+  }
+}

+ 104 - 18
sites/all/modules/contrib/panels/panels/panels.module

@@ -6,7 +6,7 @@
  * Core functionality for the Panels engine.
  */
 
-define('PANELS_REQUIRED_CTOOLS_API', '2.0.8');
+define('PANELS_REQUIRED_CTOOLS_API', '2.0.9');
 
 /**
  * The current working panels version.
@@ -20,7 +20,7 @@ define('PANELS_REQUIRED_CTOOLS_API', '2.0.8');
  *   ; Requires Panels v7.x-3.4 or newer.
  *   dependencies[] = panels (>=3.4)
  */
-define('PANELS_VERSION', '7.x-3.5');
+define('PANELS_VERSION', '7.x-3.8');
 
 
 define('PANELS_TITLE_FIXED', 0); // Hide title use to be true/false. So false remains old behavior.
@@ -302,6 +302,11 @@ function panels_permission() {
       'title' => t("Change layouts with the Panels In-Place Editor"),
       'description' => t("Allows a user to change layouts with the IPE."),
     ),
+    'bypass access in place editing' => array(
+      'title' => t("Bypass access checks when using Panels In-Place Editor"),
+      'description' => t("Allows using IPE even if user does not have additional permissions granted by other modules."),
+      'restrict access' => TRUE,
+    ),
     'administer advanced pane settings' => array(
       'title' => t("Configure advanced settings on Panel panes"),
       'description' => t(""),
@@ -312,8 +317,20 @@ function panels_permission() {
     ),
     'administer panels styles' => array(
       'title' => t("Administer Panels styles"),
+      'description' => t("DEPRECATED: Modules using this permission should use specific style permissions. See Issue #2329419 for more info."),
+    ),
+    'administer panels display styles' => array(
+      'title' => t("Administer Panels display styles"),
+      'description' => t("Allows a user to administer the styles of Panel displays."),
+    ),
+    'administer panels pane styles' => array(
+      'title' => t("Administer Panels pane styles"),
       'description' => t("Allows a user to administer the styles of Panel panes."),
     ),
+    'administer panels region styles' => array(
+      'title' => t("Administer Panels region styles"),
+      'description' => t("Allows a user to administer the styles of Panel regions."),
+    ),
     'use panels caching features' => array(
       'title' => t("Configure caching settings on Panels"),
       'description' => t("Allows a user to configure caching on Panels displays and panes."),
@@ -329,17 +346,6 @@ function panels_permission() {
   );
 }
 
-/**
- * Implementation of hook_flush_caches().
- *
- * We implement this so that we can be sure our legacy rendering state setting
- * in $conf is updated whenever caches are cleared.
- */
-//function panels_flush_caches() {
-//  $legacy = panels_get_legacy_state();
-//  $legacy->determineStatus();
-//}
-
 /**
  * Implements hook_flush_caches().
  */
@@ -404,6 +410,7 @@ function panels_ctools_plugin_type() {
     'display_renderers' => array(
       'classes' => array('renderer'),
     ),
+    'panels_storage' => array(),
   );
 }
 
@@ -658,7 +665,6 @@ function panels_edit_layout($display, $finish, $destination = NULL, $allowed_lay
 
 /**
  * Forms the basis of a panel display
- *
  */
 class panels_display {
   var $args = array();
@@ -770,6 +776,55 @@ class panels_display {
     }
     return $output;
   }
+
+  /**
+   * Determine if the given user can perform the requested operation.
+   *
+   * @param string $op
+   *   An operation like: create, read, update, or delete.
+   * @param object $account
+   *   (optional) The account to check access for.
+   *
+   * @return bool
+   *   TRUE if access is granted; otherwise FALSE.
+   */
+  function access($op, $account = NULL) {
+    global $user;
+
+    if (!$account) {
+      $account = $user;
+    }
+
+    // Even administrators need to go through the access system. However, to
+    // support legacy plugins, user 1 gets full access no matter what.
+    if ($account->uid == 1) {
+      return TRUE;
+    }
+
+    if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) {
+      return FALSE;
+    }
+
+    if (empty($this->storage_type) || empty($this->storage_id)) {
+      return FALSE;
+    }
+
+    if ($this->storage_type == 'unknown') {
+      return FALSE;
+    }
+
+    $storage_plugin = panels_get_panels_storage_plugin($this->storage_type);
+    if (!$storage_plugin) {
+      return FALSE;
+    }
+
+    $access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback');
+    if (!$access_callback) {
+      return FALSE;
+    }
+
+    return $access_callback($this->storage_type, $this->storage_id, $op, $account);
+  }
 }
 
 /**
@@ -1079,12 +1134,12 @@ function panels_export_display($display, $prefix = '') {
         $title_pid = $pid;
       }
       $pane->pid = $pid;
-      $output .= ctools_export_object('panels_pane', $pane, $prefix . '  ');
-      $output .= "$prefix  " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
+      $output .= ctools_export_object('panels_pane', $pane, $prefix);
+      $output .= $prefix . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
       if (!isset($region_counters[$pane->panel])) {
         $region_counters[$pane->panel] = 0;
       }
-      $output .= "$prefix  " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
+      $output .= $prefix . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
     }
   }
   $output .= $prefix . '$display->hide_title = ';
@@ -1334,7 +1389,7 @@ function template_preprocess_panels_pane(&$vars) {
   $vars['pane_suffix'] = !empty($content->pane_suffix) ? $content->pane_suffix : '';
 
   $vars['title'] = !empty($content->title) ? $content->title : '';
-  $vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : 'h2';
+  $vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : variable_get('override_title_heading', 'h2');
   $vars['title_attributes_array']['class'][] = 'pane-title';
 
   $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
@@ -1410,6 +1465,11 @@ function panels_ajax_router() {
   ctools_include('cleanstring');
   $renderer->clean_key = ctools_cleanstring($cache_key);
 
+  $op = $renderer->get_panels_storage_op_for_ajax($method);
+  if (!$cache->display->access($op)) {
+    return MENU_ACCESS_DENIED;
+  }
+
   $output = call_user_func_array(array($renderer, $method), $args);
 
   if (empty($output) && !empty($renderer->commands)) {
@@ -1721,6 +1781,32 @@ function panels_page_wizard_panels_cache_set($key, $cache) {
   page_manager_set_wizard_cache($wizard_cache);
 }
 
+/**
+ * Implements hook_default_page_manager_handlers_alter().
+ *
+ * If a default Panels display has no storage type, set it.
+ */
+function panels_default_page_manager_handlers_alter(&$handlers) {
+  foreach ($handlers as &$handler) {
+    if ($handler->handler == 'panel_context') {
+      $display =& $handler->conf['display'];
+      if (empty($display->storage_type)) {
+        $display->storage_type = 'page_manager';
+        $display->storage_id = $handler->name;
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_default_page_manager_pages_alter().
+ */
+function panels_default_page_manager_pages_alter(&$pages) {
+  foreach ($pages as &$page) {
+    panels_default_page_manager_handlers_alter($page->default_handlers);
+  }
+}
+
 // --------------------------------------------------------------------------
 // General utility functions
 

+ 18 - 6
sites/all/modules/contrib/panels/panels/panels_ipe/js/panels_ipe.js

@@ -2,12 +2,7 @@
 // Ensure the $ alias is owned by jQuery.
 (function($) {
 
-// randomly lock a pane.
-// @debug only
 Drupal.settings.Panels = Drupal.settings.Panels || {};
-Drupal.settings.Panels.RegionLock = {
-  10: { 'top': false, 'left': true, 'middle': true }
-}
 
 Drupal.PanelsIPE = {
   editors: {},
@@ -31,6 +26,17 @@ Drupal.PanelsIPE = {
 
 Drupal.behaviors.PanelsIPE = {
   attach: function(context) {
+    // Remove any old editors.
+    for (var i in Drupal.PanelsIPE.editors) {
+      if (Drupal.settings.PanelsIPECacheKeys.indexOf(i) === -1) {
+        // Clean-up a little bit and remove it.
+        Drupal.PanelsIPE.editors[i].editing = false;
+        Drupal.PanelsIPE.editors[i].changed = false;
+        delete Drupal.PanelsIPE.editors[i];
+      }
+    }
+
+    // Initialize new editors.
     for (var i in Drupal.settings.PanelsIPECacheKeys) {
       var key = Drupal.settings.PanelsIPECacheKeys[i];
       $('div#panels-ipe-display-' + key + ':not(.panels-ipe-processed)')
@@ -213,6 +219,8 @@ function DrupalPanelsIPE(cache_key, cfg) {
 
     $('div.panels-ipe-sort-container', ipe.topParent).bind('sortstop', this.enableRegions);
 
+    // Refresh the control jQuery object.
+    ipe.control = $(ipe.control.selector);
     $('.panels-ipe-form-container', ipe.control).append(formdata);
 
     $('input:submit:not(.ajax-processed), button:not(.ajax-processed)', ipe.control).addClass('ajax-processed').each(function() {
@@ -233,7 +241,7 @@ function DrupalPanelsIPE(cache_key, cfg) {
     // it clears out inline styles.
     $('.panels-ipe-on').show();
     ipe.showForm();
-    ipe.topParent.addClass('panels-ipe-editing');
+    $('body').add(ipe.topParent).addClass('panels-ipe-editing');
 
   };
 
@@ -264,6 +272,10 @@ function DrupalPanelsIPE(cache_key, cfg) {
     // Re-show all the IPE non-editing meta-elements
     $('div.panels-ipe-off').show('fast');
 
+    // Refresh the container and control jQuery objects.
+    ipe.container = $(ipe.container.selector);
+    ipe.control = $(ipe.control.selector);
+
     ipe.showButtons();
     // Re-hide all the IPE meta-elements
     $('div.panels-ipe-on').hide();

+ 31 - 0
sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.api.php

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by Panels In-Place Editor.
+ */
+
+/**
+ * Allow modules to control access to the Panels IPE.
+ *
+ * @param panels_display $display
+ *   The panels display about to be rendered.
+ *
+ * @return TRUE|FALSE|NULL
+ *   Returns TRUE to allow access, FALSE to deny, or NULL if the module
+ *   implementing this hook doesn't care about access for the given display.
+ */
+function hook_panels_ipe_access(panels_display $display) {
+  // We only care about displays with the 'panelizer' context.
+  if (!isset($display->context['panelizer'])) {
+    return NULL;
+  }
+
+  if ($display->context['panelizer']->type[0] == 'entity:node') {
+    // Allow or deny IPE access based on node type.
+    return $display->context['panelizer']->data->type == 'awesome_page';
+  }
+
+  // Otherwise, deny access to everything!
+  return FALSE;
+}

+ 3 - 4
sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.info

@@ -1,15 +1,14 @@
 name = Panels In-Place Editor
 description = Provide a UI for managing some Panels directly on the frontend, instead of having to use the backend.
 package = "Panels"
-version = PANELS_VERSION
 dependencies[] = panels
 core = 7.x
 configure = admin/structure/panels
 files[] = panels_ipe.module
 
-; Information added by Drupal.org packaging script on 2015-01-28
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-3.8"
 core = "7.x"
 project = "panels"
-datestamp = "1422472985"
+datestamp = "1476582295"
 

+ 5 - 2
sites/all/modules/contrib/panels/panels/panels_ipe/panels_ipe.module

@@ -96,7 +96,7 @@ function template_preprocess_panels_ipe_pane_wrapper(&$vars) {
   }
 
   // Add option to configure style in IPE
-  if (user_access('administer panels styles')) {
+  if (user_access('administer panels pane styles')) {
     $vars['links']['style'] = array(
       'title' => '<span>' . t('Style') . '</span>',
       'href' => $renderer->get_url('style-type', 'pane', $pane->pid),
@@ -167,7 +167,7 @@ function template_preprocess_panels_ipe_add_pane_button(&$vars) {
   $vars['links'] = '';
 
   // Add option to configure style in IPE
-  if (user_access('administer panels styles')) {
+  if (user_access('administer panels region styles')) {
     $vars['links']['style'] = array(
       'title' => '<span>' . t('Region style') . '</span>',
       'href' => $renderer->get_url('style-type', 'region', $region_id),
@@ -221,6 +221,7 @@ function panels_ipe_get_cache_key($key = NULL) {
  */
 function panels_ipe_toolbar_add_button($cache_key, $id, $button) {
   $buttons = &drupal_static('panels_ipe_toolbar_buttons', array());
+  drupal_alter('panels_ipe_button', $button, $id, $cache_key);
   $buttons[$cache_key][$id] = $button;
 }
 
@@ -246,9 +247,11 @@ function panels_ipe_page_alter(&$page) {
 
 function theme_panels_ipe_toolbar($vars) {
   $buttons = $vars['buttons'];
+  ctools_include('cleanstring');
 
   $output = "<div id='panels-ipe-control-container' class='clearfix'>";
   foreach ($buttons as $key => $ipe_buttons) {
+    $key = ctools_cleanstring($key);
     $output .= "<div id='panels-ipe-control-$key' class='panels-ipe-control'>";
 
     // Controls in this container will appear when the IPE is not on.

+ 70 - 3
sites/all/modules/contrib/panels/panels/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php

@@ -7,12 +7,44 @@ class panels_renderer_ipe extends panels_renderer_editor {
   // The IPE operates in normal render mode, not admin mode.
   var $admin = FALSE;
 
+  // Whether or not the user has access.
+  var $access = NULL;
+
+  function invoke_panels_ipe_access() {
+    if (user_access('bypass access in place editing')) {
+      return TRUE;
+    }
+    // Modules can return TRUE, FALSE or NULL, for allowed, disallowed,
+    // or don't care - respectively. On the first FALSE, we deny access,
+    // otherwise allow.
+    foreach (module_invoke_all('panels_ipe_access', $this->display) as $result) {
+      if ($result === FALSE) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  function access() {
+    if (is_null($this->access)) {
+      $this->access = $this->invoke_panels_ipe_access();
+    }
+    return $this->access;
+  }
+
   function render() {
     $output = parent::render();
-    return "<div id='panels-ipe-display-{$this->clean_key}' class='panels-ipe-display-container'>$output</div>";
+    if ($this->access()) {
+      return "<div id='panels-ipe-display-{$this->clean_key}' class='panels-ipe-display-container'>$output</div>";
+    }
+    return $output;
   }
 
   function add_meta() {
+    if (!$this->access()) {
+      return parent::add_meta();
+    }
+
     ctools_include('display-edit', 'panels');
     ctools_include('content');
 
@@ -42,7 +74,7 @@ class panels_renderer_ipe extends panels_renderer_editor {
       '#suffix' => '</div>',
     );
 
-    panels_ipe_toolbar_add_button($this->clean_key, 'panels-ipe-startedit', $button);
+    panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-startedit', $button);
 
     // @todo this actually should be an IPE setting instead.
     if (user_access('change layouts in place editing')) {
@@ -63,7 +95,7 @@ class panels_renderer_ipe extends panels_renderer_editor {
       '#suffix' => '</div>',
       );
 
-      panels_ipe_toolbar_add_button($this->clean_key, 'panels-ipe-change-layout', $button);
+      panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-change-layout', $button);
     }
 
     ctools_include('ajax');
@@ -72,6 +104,7 @@ class panels_renderer_ipe extends panels_renderer_editor {
 
     ctools_add_css('panels_dnd', 'panels');
     ctools_add_css('panels_admin', 'panels');
+    ctools_add_js('panels-base', 'panels');
     ctools_add_js('panels_ipe', 'panels_ipe');
     ctools_add_css('panels_ipe', 'panels_ipe');
 
@@ -95,6 +128,9 @@ class panels_renderer_ipe extends panels_renderer_editor {
     if (empty($output)) {
       return;
     }
+    if (!$this->access()) {
+      return $output;
+    }
 
     // If there are region locks, add them.
     if (!empty($pane->locks['type']) && $pane->locks['type'] == 'regions') {
@@ -137,6 +173,10 @@ class panels_renderer_ipe extends panels_renderer_editor {
   }
 
   function prepare_panes($panes) {
+    if (!$this->access()) {
+      return parent::prepare_panes($panes);
+    }
+
     // Set to admin mode just for this to ensure all panes are represented.
     $this->admin = TRUE;
     $panes = parent::prepare_panes($panes);
@@ -144,6 +184,10 @@ class panels_renderer_ipe extends panels_renderer_editor {
   }
 
   function render_pane_content(&$pane) {
+    if (!$this->access()) {
+      return parent::render_pane_content($pane);
+    }
+
     if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
       $content = parent::render_pane_content($pane);
     }
@@ -174,6 +218,10 @@ class panels_renderer_ipe extends panels_renderer_editor {
    * @param $panes
    */
   function render_region($region_id, $panes) {
+    if (!$this->access()) {
+      return parent::render_region($region_id, $panes);
+    }
+
     // Generate this region's 'empty' placeholder pane from the IPE plugin.
     $empty_ph = theme('panels_ipe_placeholder_pane', array('region_id' => $region_id, 'region_title' => $this->plugins['layout']['regions'][$region_id]));
 
@@ -211,6 +259,19 @@ class panels_renderer_ipe extends panels_renderer_editor {
       // Break the lock.
       panels_edit_cache_break_lock($this->cache);
     }
+  } 
+
+  function get_panels_storage_op_for_ajax($method) {
+    switch ($method) {
+      case 'ajax_unlock_ipe':
+      case 'ajax_save_form':
+        return 'update';
+      case 'ajax_change_layout':
+      case 'ajax_set_layout':
+        return 'change layout';
+    }
+
+    return parent::get_panels_storage_op_for_ajax($method);
   }
 
   /**
@@ -284,6 +345,9 @@ class panels_renderer_ipe extends panels_renderer_editor {
       // rendered.
       $this->meta_location = 'inline';
       $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
+      $buttons = &drupal_static('panels_ipe_toolbar_buttons', array());
+      $output = theme('panels_ipe_toolbar', array('buttons' => $buttons));
+      $this->commands[] = ajax_command_replace('#panels-ipe-control-container', $output);
     }
     else {
       // Cancelled. Clear the cache.
@@ -322,6 +386,9 @@ class panels_renderer_ipe extends panels_renderer_editor {
     // Filter out builders
     $layouts = array_filter($layouts, '_panels_builder_filter');
 
+    // Let other modules filter the layouts.
+    drupal_alter('panels_layouts_available', $layouts);
+
     // Define the current layout
     $current_layout = $this->plugins['layout']['name'];
 

+ 3 - 4
sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.info

@@ -1,13 +1,12 @@
 name = Mini panels
 description = Create mini panels that can be used as blocks by Drupal and panes by other panel modules.
 package = "Panels"
-version = PANELS_VERSION
 dependencies[] = panels
 core = 7.x
 files[] = plugins/export_ui/panels_mini_ui.class.php
-; Information added by Drupal.org packaging script on 2015-01-28
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-3.8"
 core = "7.x"
 project = "panels"
-datestamp = "1422472985"
+datestamp = "1476582295"
 

+ 59 - 1
sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.install

@@ -114,7 +114,7 @@ function panels_mini_uninstall() {
     $deltas[] = $panel_mini->pid;
   }
 
-  if ($deltas) {
+  if (db_table_exists('block') && $deltas) {
     // Delete all configured blocks.
     db_delete('block')
       ->condition('module', 'panels_mini')
@@ -122,3 +122,61 @@ function panels_mini_uninstall() {
       ->execute();
   }
 }
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function panels_mini_update_dependencies() {
+  // Update 7301 requires panels storage support
+  $dependencies['panels_mini'][7301] = array(
+    'panels' => 7305,
+  );
+
+  return $dependencies;
+}
+
+/**
+ * Set the storage type and id on existing mini panels.
+ */
+function panels_mini_update_7301() {
+  if (!isset($sandbox['progress'])) {
+     // Initialize batch update information.
+     $sandbox['progress'] = (float)0;
+     $sandbox['current_did'] = -1;
+     $sandbox['max'] = db_query("SELECT COUNT(pd.did)
+         FROM {panels_display} pd
+           JOIN {panels_mini} pm ON pm.did = pd.did
+         WHERE pd.storage_type = ''")->fetchField();
+   }
+
+  // Set a limit of how many rows to process per batch.
+  $limit = 1000;
+
+  // Run the query
+  $result = db_query_range("SELECT pd.did, pm.name
+      FROM {panels_display} pd
+        JOIN {panels_mini} pm ON pm.did = pd.did
+      WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did']));
+
+  foreach ($result as $row) {
+    db_update('panels_display')
+      ->fields(array(
+        'storage_type' => 'panels_mini',
+        'storage_id' => $row->name,
+      ))
+      ->condition('did', $row->did)
+      ->execute();
+
+    // Update our progress information.
+    $sandbox['progress']++;
+    $sandbox['current_did'] = $row->did;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    return t('Added the storage type for panels_mini to relevant panels displays');
+  }
+}

+ 76 - 13
sites/all/modules/contrib/panels/panels/panels_mini/panels_mini.module

@@ -116,6 +116,7 @@ function panels_mini_block_view($delta = 0) {
 
   $panel_mini->context = $panel_mini->display->context =  ctools_context_load_contexts($panel_mini, FALSE, $contexts);
   $panel_mini->display->css_id = panels_mini_get_id($panel_mini->name);
+  $panel_mini->display->owner = $panel_mini;
 
   $block = array();
 
@@ -146,6 +147,10 @@ function panels_mini_block_list_alter(&$blocks) {
   if (module_exists('page_manager')) {
     $current_page = page_manager_get_current_page();
   }
+
+  // Load add at once to save time.
+  panels_mini_load_all();
+
   foreach ($blocks as $key => $block) {
     if ($block->module != 'panels_mini') {
       // This block was added by a contrib module, leave it in the list.
@@ -254,23 +259,31 @@ function panels_mini_load($name) {
   // We use array_key_exists because failed loads will be NULL and
   // isset() will try to load it again.
   if (!array_key_exists($name, $cache)) {
-    ctools_include('export');
-    $result = ctools_export_load_object('panels_mini', 'names', array($name));
-    if (isset($result[$name])) {
-      if (empty($result[$name]->display)) {
-        $result[$name]->display = panels_load_display($result[$name]->did);
-        if (!empty($result[$name]->title) && empty($result[$name]->display->title)) {
-          $result[$name]->display->title = $result[$name]->title;
+    $cid = 'panels_mini_load:' . $name;
+    $result = cache_get($cid, 'cache_panels');
+    if (!empty($result->data)) {
+      $cache[$name] = $result->data;
+    }
+    else {
+      ctools_include('export');
+      $result = ctools_export_load_object('panels_mini', 'names', array($name));
+      if (isset($result[$name])) {
+        if (empty($result[$name]->display)) {
+          $result[$name]->display = panels_load_display($result[$name]->did);
+          if (!empty($result[$name]->title) && empty($result[$name]->display->title)) {
+            $result[$name]->display->title = $result[$name]->title;
+          }
         }
+        $cache[$name] = $result[$name];
+        if (!empty($result[$name]->title) && empty($result[$name]->admin_title)) {
+          $cache[$name]->admin_title = $result[$name]->title;
+        }
+        cache_set($cid, $cache[$name], 'cache_panels', CACHE_TEMPORARY);
       }
-      $cache[$name] = $result[$name];
-      if (!empty($result[$name]->title) && empty($result[$name]->admin_title)) {
-        $cache[$name]->admin_title = $result[$name]->title;
+      else {
+        $cache[$name] = NULL;
       }
     }
-    else {
-      $cache[$name] = NULL;
-    }
   }
 
   if (isset($cache[$name])) {
@@ -292,6 +305,22 @@ function panels_mini_load_all($reset = FALSE) {
     if ($reset) {
       $cache = array();
     }
+    else {
+      $panel_names = db_select('panels_mini', 'pm')
+        ->fields('pm', array('name'))
+        ->execute();
+      $cids = array();
+      foreach ($panel_names as $name) {
+        $cids[] = 'panels_mini_load:' . $name->name;
+      }
+      $output = cache_get_multiple($cids, 'cache_panels');
+      foreach ($output as $mini) {
+        if (!empty($mini->data)) {
+          $mini = $mini->data;
+          $cache[$mini->name] = $mini;
+        }
+      }
+    }
 
     ctools_include('export');
     $minis = ctools_export_load_object('panels_mini');
@@ -333,10 +362,14 @@ function panels_mini_load_all($reset = FALSE) {
  */
 function panels_mini_save(&$mini) {
   if (!empty($mini->display)) {
+    $mini->display->storage_id = $mini->name;
     $display = panels_save_display($mini->display);
     $mini->did = $display->did;
   }
 
+  // Clear the panels_mini_load cache.
+  cache_clear_all('panels_mini_load:', 'cache_panels', TRUE);
+
   $update = (isset($mini->pid) && $mini->pid != 'new') ? array('pid') : array();
   drupal_write_record('panels_mini', $mini, $update);
 
@@ -389,6 +422,24 @@ function panels_mini_ctools_plugin_directory($module, $plugin) {
   if ($module == 'ctools' && ($plugin == 'content_types' || $plugin == 'export_ui')) {
     return 'plugins/' . $plugin;
   }
+  if ($module == 'panels' && $plugin == 'panels_storage') {
+    return 'plugins/' . $plugin;
+  }
+}
+
+/**
+ * Implements hook_default_panels_mini_alter().
+ *
+ * If a default Panels display has no storage type, set it.
+ */
+function panels_default_panels_mini_alter(&$mini_panels) {
+  foreach ($mini_panels as &$mini_panel) {
+    $display =& $mini_panel->display;
+    if (empty($display->storage_type)) {
+      $display->storage_type = 'panels_mini';
+      $display->storage_id = $mini_panel->name;
+    }
+  }
 }
 
 /**
@@ -427,6 +478,9 @@ function panels_mini_panels_cache_get($key) {
   $cache->display = $item->display;
   $cache->display->context = ctools_context_load_contexts($item);
   $cache->display->cache_key = 'panels_mini:' . $key;
+  $cache->display->storage_type = 'panels_mini';
+  // Temporary storage id that's replaced in panels_mini_save().
+  $cache->display->storage_id = 'panels_mini';
   $cache->content_types = panels_common_get_allowed_types('panels_mini', $cache->display->context);
   $cache->display_title = TRUE;
 
@@ -470,6 +524,15 @@ function panels_mini_panels_cache_save($key, $cache) {
 function panels_mini_panels_cache_break_lock($key, $cache) {
 }
 
+/**
+ * Implements hook_panels_pre_render().
+ */
+function panels_mini_panels_pre_render($display, $renderer) {
+  if (isset($display->owner->table) && $display->owner->table == 'panels_mini' && $renderer instanceof panels_renderer_standard) {
+    $renderer->show_empty_layout = FALSE;
+  }
+}
+
 /**
  * Implementation of hook_panels_dashboard_blocks().
  *

+ 8 - 3
sites/all/modules/contrib/panels/panels/panels_mini/plugins/content_types/panels_mini.inc

@@ -4,7 +4,7 @@
  * @file
  * Contains the content type plugin for a mini panel. While this does not
  * need to be broken out into a .inc file, it's convenient that we do so
- * that we don't load code unneccessarily. Plus it demonstrates plugins
+ * that we don't load code unnecessarily. Plus it demonstrates plugins
  * in modules other than Panels itself.
  *
  */
@@ -68,8 +68,13 @@ function _panels_mini_panels_mini_content_type_content_type($mini) {
     $type['required context'] = array();
     foreach ($mini->requiredcontexts as $context) {
       $info = ctools_get_context($context['name']);
-      // TODO: allow an optional setting
-      $type['required context'][] = new ctools_context_required($context['identifier'], $info['context name']);
+      // Check if the required context is actually required.
+      if (!empty($context['optional'])) {
+        $type['required context'][] = new ctools_context_optional($context['identifier'], $info['context name']);
+      }
+      else {
+        $type['required context'][] = new ctools_context_required($context['identifier'], $info['context name']);
+      }
     }
   }
   return $type;

+ 22 - 0
sites/all/modules/contrib/panels/panels/panels_mini/plugins/panels_storage/panels_mini.inc

@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Provides a panels_storage plugin for mini panels.
+ */
+
+// Plugin definition
+$plugin = array(
+  'access callback' => 'panels_mini_panels_storage_access',
+);
+
+/**
+ * Access callback for panels storage.
+ */
+function panels_mini_panels_storage_access($storage_type, $storage_id, $op, $account) {
+  if ($op == 'create') {
+    return user_access('create mini panels', $account);
+  }
+
+  return user_access('administer mini panels', $account);
+}

+ 3 - 4
sites/all/modules/contrib/panels/panels/panels_node/panels_node.info

@@ -1,15 +1,14 @@
 name = Panel nodes
 description = Create nodes that are divided into areas with selectable content.
 package = "Panels"
-version = PANELS_VERSION
 dependencies[] = panels
 configure = admin/structure/panels
 core = 7.x
 files[] = panels_node.module
 
-; Information added by Drupal.org packaging script on 2015-01-28
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-3.8"
 core = "7.x"
 project = "panels"
-datestamp = "1422472985"
+datestamp = "1476582295"
 

+ 96 - 0
sites/all/modules/contrib/panels/panels/panels_node/panels_node.install

@@ -56,6 +56,18 @@ function panels_node_uninstall() {
   drupal_uninstall_schema('panels_node');
 }
 
+/**
+ * Implements hook_update_dependencies().
+ */
+function panels_node_update_dependencies() {
+  // Update 7301 requires panels storage support
+  $dependencies['panels_node'][7301] = array(
+    'panels' => 7305,
+  );
+
+  return $dependencies;
+}
+
 /**
  * Implementation of hook_update to handle adding a pipeline
  */
@@ -69,3 +81,87 @@ function panels_node_update_6001() {
   db_add_field('panels_node', 'pipeline', $field);
   return $ret;
 }
+
+/**
+ * Migrate legacy Drupal 6 permissions to Drupal 7.
+ */
+function panels_node_update_7301() {
+  $permissions = array(
+    'create panel-nodes' => 'create panel content',
+    'edit any panel-nodes' => 'edit any panel content',
+    'edit own panel-nodes' => 'edit own panel content',
+    'delete any panel-nodes' => 'delete any panel content',
+    'delete own panel-nodes' => 'delete own panel content',
+  );
+  foreach ($permissions as $legacy_permission => $new_permission) {
+    $query = db_select('role_permission', 'p')
+      ->fields('p', array('rid'))
+      ->condition('permission', $legacy_permission);
+    $rids = $query->execute()->fetchCol();
+    foreach ($rids as $rid) {
+      // Insert the new permission if it doesn't already exist.
+      db_merge('role_permission')
+        ->key(array(
+          'rid' => $rid,
+          'permission' => $new_permission,
+        ))
+        ->insertFields(array(
+          'rid' => $rid,
+          'permission' => $new_permission,
+          'module' => 'node',
+        ))
+        ->execute();
+    }
+
+    // Delete the legacy permission.
+    db_delete('role_permission')
+      ->condition('permission', $legacy_permission)
+      ->execute();
+  }
+}
+
+/**
+ * Set the storage type and id on existing panels nodes.
+ */
+function panels_node_update_7302() {
+  if (!isset($sandbox['progress'])) {
+     // Initialize batch update information.
+     $sandbox['progress'] = (float)0;
+     $sandbox['current_did'] = -1;
+     $sandbox['max'] = db_query("SELECT COUNT(pd.did)
+         FROM {panels_display} pd
+           JOIN {panels_node} pn ON pn.did = pd.did
+         WHERE pd.storage_type = ''")->fetchField();
+   }
+
+  // Set a limit of how many rows to process per batch.
+  $limit = 1000;
+
+  // Run the query
+  $result = db_query_range("SELECT pd.did, pn.nid
+      FROM {panels_display} pd
+        JOIN {panels_node} pn ON pn.did = pd.did
+      WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did']));
+
+  foreach ($result as $row) {
+    db_update('panels_display')
+      ->fields(array(
+        'storage_type' => 'panels_node',
+        'storage_id' => $row->nid,
+      ))
+      ->condition('did', $row->did)
+      ->execute();
+
+    // Update our progress information.
+    $sandbox['progress']++;
+    $sandbox['current_did'] = $row->did;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    return t('Added the storage type for panels_node to relevant panels displays');
+  }
+}

+ 36 - 40
sites/all/modules/contrib/panels/panels/panels_node/panels_node.module

@@ -17,33 +17,22 @@
  */
 function panels_node_permission() {
   return array(
-    'create panel-nodes' => array(
-      'title' => t('Create panel nodes'),
-      'description' => t('Create new panel nodes.'),
-    ),
-    'edit any panel-nodes' => array(
-      'title' => t('Edit any panel-nodes'),
-      'description' => t('Edit all pre-existing panel nodes regardless of ownership.'),
-    ),
-    'edit own panel-nodes' => array(
-      'title' => t('Edit own panel nodes'),
-      'description' => t('Edit panel nodes owned by this user.'),
-    ),
     'administer panel-nodes' => array(
       'title' => t('Administer panel nodes'),
       'description' => t('Full administrative access to panel nodes including create, update and delete all'),
     ),
-    'delete any panel-nodes' => array(
-      'title' => t('Delete any panel nodes'),
-      'description' => t('Delete any panel node regardless of ownership'),
-    ),
-    'delete own panel-nodes' => array(
-      'title' => t('Delete own panel nodes'),
-      'description' => t('Delete any panel node owned by this user.'),
-    ),
   );
 }
 
+/**
+ * Implementation of hook_ctools_plugin_directory().
+ */
+function panels_node_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'panels' && $plugin == 'panels_storage') {
+    return 'plugins/' . $plugin;
+  }
+}
+
 /**
  * Implementation of hook_menu().
  */
@@ -83,7 +72,7 @@ function panels_node_menu() {
 
   $items['node/add/panel/choose-layout'] = array(
     'title' => 'Choose layout',
-    'access arguments' => array('create panel-nodes'),
+    'access callback' => 'panels_add_panel_access_callback',
     'page callback' => 'panels_node_add',
     'type' => MENU_CALLBACK,
   );
@@ -102,6 +91,13 @@ function panels_node_edit_node($node) {
   return node_access('update', $node);
 }
 
+/**
+ * Access callback to determine if user has access to add panel nodes.
+ */
+function panels_add_panel_access_callback() {
+  return user_access('create panel content') || user_access('administer panel-nodes');
+}
+
 /**
  * Override of node add page to force layout selection prior
  * to actually editing a node.
@@ -113,7 +109,7 @@ function panels_node_add() {
   ctools_include('common', 'panels');
 
   $layouts = panels_common_get_allowed_layouts('panels_node');
-  return panels_common_print_layout_links($layouts, 'node/add/panel', array('query' => $_GET));
+  return panels_common_print_layout_links($layouts, 'node/add/panel', array('query' => drupal_get_query_parameters()));
 }
 
 // ---------------------------------------------------------------------------
@@ -156,19 +152,6 @@ function panels_node_node_access($node, $op, $account) {
   if (user_access('administer panel-nodes', $account)) {
     return NODE_ACCESS_ALLOW;
   }
-
-  if ($op == 'create' && user_access('create panel-nodes', $account)) {
-    return NODE_ACCESS_ALLOW;
-  }
-
-  if ($op == 'update' && (user_access('edit any panel-nodes', $account) || $node->uid == $account->uid && user_access('edit own panel-nodes', $account))) {
-    return NODE_ACCESS_ALLOW;
-  }
-
-
-  if ($op == 'delete' && (user_access('delete any panel-nodes') || $node->uid == $account->uid && user_access('delete own panel-nodes'))) {
-    return NODE_ACCESS_ALLOW;
-  }
 }
 
 /**
@@ -183,14 +166,12 @@ function panels_node_hook_form(&$node, &$form_state) {
     // and if that doesn't work present them with a list to pick from.
     $panel_layout = isset($node->panel_layout) ? $node->panel_layout : arg(3);
     if (empty($panel_layout)) {
-      $opts = $_GET;
-      unset($opts['q']);
-      return drupal_goto('node/add/panel/choose-layout', $opts);
+      drupal_goto('node/add/panel/choose-layout', array('query' => drupal_get_query_parameters()));
     }
 
     $layout = panels_get_layout($panel_layout);
     if (empty($layout)) {
-      return drupal_not_found();
+      return MENU_NOT_FOUND;
     }
     $form['panels_node']['layout'] = array(
       '#type' => 'value',
@@ -233,7 +214,7 @@ function panels_node_hook_form(&$node, &$form_state) {
     '#type' => 'radios',
     '#options' => $options,
     '#title' => t('Renderer'),
-    '#default_value' => isset($node->panels_node['pipeline']) ? $node->panels_node['pipeline'] : 'standard',
+    '#default_value' => isset($node->panels_node['pipeline']) ? $node->panels_node['pipeline'] : variable_get('panels_renderer_default', 'standard'),
   );
 
   return $form;
@@ -271,6 +252,8 @@ function panels_node_hook_insert(&$node) {
   // Create a new display and record that.
   $display = panels_new_display();
   $display->layout = $node->panels_node['layout'];
+  $display->storage_type = 'panels_node';
+  $display->storage_id = $node->nid;
 
   // Special handling for nodes being imported from an export.module data dump.
   if (!empty($node->export_display)) {
@@ -431,6 +414,19 @@ function panels_node_panels_dashboard_blocks(&$vars) {
   );
 }
 
+/**
+ * Implements hook_panels_ipe_access().
+ */
+function panels_node_panels_ipe_access($display) {
+  // We only care about Panels displays from panels_node.
+  if (isset($display->context['panel-node'])) {
+    // Only allow access to use the IPE if the user has 'update' access to
+    // the underlying node.
+    $node = $display->context['panel-node']->data;
+    return node_access('update', $node); 
+  }
+}
+
 // ---------------------------------------------------------------------------
 // Callbacks for panel caching.
 

+ 25 - 0
sites/all/modules/contrib/panels/panels/panels_node/plugins/panels_storage/panels_node.inc

@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Provides a panels_storage plugin for panels node.
+ */
+
+// Plugin definition
+$plugin = array(
+  'access callback' => 'panels_node_panels_storage_access',
+);
+
+/**
+ * Access callback for panels storage.
+ */
+function panels_node_panels_storage_access($storage_type, $storage_id, $op, $account) {
+  if ($node = node_load($storage_id)) {
+    if ($op == 'read') {
+      $op = 'view';
+    }
+    return node_access($op, $node, $account);
+  }
+
+  return FALSE;
+}

+ 45 - 5
sites/all/modules/contrib/panels/panels/plugins/display_renderers/panels_renderer_editor.class.php

@@ -61,6 +61,7 @@ class panels_renderer_editor extends panels_renderer_standard {
       ctools_add_js('display_editor', 'panels');
       ctools_add_css('panels_dnd', 'panels');
       ctools_add_css('panels_admin', 'panels');
+      drupal_add_library('system', 'ui');
     }
   }
 
@@ -217,7 +218,7 @@ class panels_renderer_editor extends panels_renderer_standard {
   function get_display_links() {
     $links = array();
 
-    if (user_access('administer panels styles')) {
+    if (user_access('administer panels display styles')) {
       $style_links = $this->get_style_links('display');
       $links[] = array(
         'title' => '<span class="dropdown-header">' . t('Style') . '</span>' . theme_links(array('links' => $style_links, 'attributes' => array(), 'heading' => array())),
@@ -279,7 +280,7 @@ class panels_renderer_editor extends panels_renderer_standard {
       ),
     );
 
-    if (user_access('administer panels styles')) {
+    if (user_access('administer panels region styles')) {
       $links[] = array(
         'title' => '<hr />',
         'html' => TRUE,
@@ -513,6 +514,40 @@ class panels_renderer_editor extends panels_renderer_standard {
     return $url;
   }
 
+  /**
+   * Get the Panels storage oparation for a given renderer AJAX method.
+   *
+   * @param string $method
+   *   The method name.
+   *
+   * @return string
+   *   The Panels storage op.
+   */
+  function get_panels_storage_op_for_ajax($method) {
+    switch ($method) {
+      case 'ajax_show':
+      case 'ajax_hide':
+      case 'ajax_select_content':
+      case 'ajax_add_pane':
+      case 'ajax_edit_pane':
+      case 'ajax_panel_title':
+      case 'ajax_cache_method':
+      case 'ajax_cache_settings':
+      case 'ajax_style_type':
+      case 'ajax_style_settings':
+      case 'ajax_pane_css':
+      case 'ajax_lock':
+      case 'ajax_access_settings':
+      case 'ajax_access_add_test':
+      case 'ajax_access_configure_test':
+      case 'ajax_layout':
+      case 'ajax_style':
+        return 'update';
+    }
+
+    return parent::get_panels_storage_op_for_ajax($method);
+  }
+
   /**
    * AJAX command to show a pane.
    */
@@ -560,6 +595,11 @@ class panels_renderer_editor extends panels_renderer_standard {
       $output = theme('panels_add_content_modal', array('renderer' => $this, 'categories' => $categories, 'category' => $category, 'region' => $region));
     }
     $this->commands[] = ctools_modal_command_display($title, $output);
+
+    // Give keybord focus to the first item in the category we just loaded.
+    if (!empty($category)) {
+      $this->commands[] = ajax_command_invoke(".panels-add-content-modal .panels-section-columns :focusable:first", 'focus');
+    }
   }
 
   /**
@@ -1500,7 +1540,7 @@ function panels_ajax_edit_pane_next(&$form_state) {
 }
 
 /**
- * Handle the 'finish' click on teh add/edit pane form wizard.
+ * Handle the 'finish' click on the add/edit pane form wizard.
  *
  * All we need to do is set a flag so the return can handle adding
  * the pane.
@@ -1773,13 +1813,13 @@ function panels_edit_configure_pane_css_form($form, &$form_state) {
     '#type' => 'textfield',
     '#default_value' => isset($pane->css['css_id']) ? $pane->css['css_id'] : '',
     '#title' => t('CSS ID'),
-    '#description' => t('CSS ID to apply to this pane. This may be blank.'),
+    '#description' => t('CSS ID to apply to this pane. This may be blank. Keywords from context are allowed.'),
   );
   $form['css_class'] = array(
     '#type' => 'textfield',
     '#default_value' => isset($pane->css['css_class']) ? $pane->css['css_class'] : '',
     '#title' => t('CSS class'),
-    '#description' => t('CSS class to apply to this pane. This may be blank.'),
+    '#description' => t('CSS class to apply to this pane. This may be blank. Keywords from context are allowed.'),
   );
 
   $form['next'] = array(

+ 44 - 2
sites/all/modules/contrib/panels/panels/plugins/display_renderers/panels_renderer_standard.class.php

@@ -151,6 +151,15 @@ class panels_renderer_standard {
    */
   var $suffix = '';
 
+  /**
+   * Boolean flag indicating whether to render the layout even if all rendered
+   * regions are blank. If FALSE, the layout renders as an empty string (without
+   * prefix or suffix) if not in administrative mode.
+   *
+   * @var bool
+   */
+  var $show_empty_layout = TRUE;
+
   /**
    * Receive and store the display object to be rendered.
    *
@@ -176,6 +185,19 @@ class panels_renderer_standard {
     }
   }
 
+  /**
+   * Get the Panels storage oparation for a given renderer AJAX method.
+   *
+   * @param string $method
+   *   The method name.
+   *
+   * @return string
+   *   The Panels storage op.
+   */
+  function get_panels_storage_op_for_ajax($method) {
+    return 'read';
+  }
+
   /**
    * Prepare the attached display for rendering.
    *
@@ -396,6 +418,22 @@ class panels_renderer_standard {
       $theme = $this->plugins['layout']['theme'];
     }
 
+    // Determine whether to render layout.
+    $show = TRUE;
+    if (!$this->show_empty_layout && !$this->admin) {
+      $show = FALSE;
+      // Render layout if any region is not empty.
+      foreach ($this->rendered['regions'] as $region) {
+        if (is_string($region) && $region !== '') {
+          $show = TRUE;
+          break;
+        }
+      }
+    }
+    if (!$show) {
+      return;
+    }
+
     $this->rendered['layout'] = theme($theme, array('css_id' => check_plain($this->display->css_id), 'content' => $this->rendered['regions'], 'settings' => $this->display->layout_settings, 'display' => $this->display, 'layout' => $this->plugins['layout'], 'renderer' => $this));
     return $this->prefix . $this->rendered['layout'] . $this->suffix;
   }
@@ -474,6 +512,7 @@ class panels_renderer_standard {
    *   The array of rendered panes, keyed on pane pid.
    */
   function render_panes() {
+    drupal_alter('panels_prerender_panes', $this);
     ctools_include('content');
 
     // First, render all the panes into little boxes.
@@ -581,12 +620,14 @@ class panels_renderer_standard {
     if (!empty($content)) {
       // Pass long the css_id that is usually available.
       if (!empty($pane->css['css_id'])) {
-        $content->css_id = check_plain($pane->css['css_id']);
+        $id = ctools_context_keyword_substitute($pane->css['css_id'], array(), $this->display->context);
+        $content->css_id = check_plain($id);
       }
 
       // Pass long the css_class that is usually available.
       if (!empty($pane->css['css_class'])) {
-        $content->css_class = check_plain($pane->css['css_class']);
+        $class = ctools_context_keyword_substitute($pane->css['css_class'], array(), $this->display->context, array('css safe' => TRUE));
+        $content->css_class = check_plain($class);
       }
     }
 
@@ -601,6 +642,7 @@ class panels_renderer_standard {
    *   An array of rendered panel regions, keyed on the region name.
    */
   function render_regions() {
+    drupal_alter('panels_prerender_regions', $this);
     $this->rendered['regions'] = array();
 
     // Loop through all panel regions, put all panes that belong to the current

+ 5 - 0
sites/all/modules/contrib/panels/panels/plugins/page_wizards/landing_page.inc

@@ -66,6 +66,8 @@ function panels_landing_page_new_page(&$cache) {
   );
   $cache->display = panels_new_display();
   $cache->display->layout = 'flexible';
+  $cache->display->storage_type = 'page_manager';
+  $cache->display->storage_id = 'new';
 }
 
 /**
@@ -257,6 +259,9 @@ function panels_landing_page_finish(&$form_state) {
   // Create the the panel context variant configured with our display
   $plugin = page_manager_get_task_handler('panel_context');
 
+  // Set the storage id.
+  $cache->display->storage_id = $cache->name;
+
   // Create a new handler.
   $handler = page_manager_new_task_handler($plugin);
   $handler->conf['title'] = t('Landing page');

+ 19 - 0
sites/all/modules/contrib/panels/panels/plugins/panels_storage/page_manager.inc

@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Provides a panels_storage plugin for page_manager.
+ */
+
+// Plugin definition
+$plugin = array(
+  'access callback' => 'page_manager_panels_storage_access',
+);
+
+/**
+ * Access callback for panels storage.
+ */
+function page_manager_panels_storage_access($storage_type, $storage_id, $op, $account) {
+  // Only users with the 'use page manager' or administer page manager perms.
+  return user_access('use page manager', $account) || user_access('administer page manager', $account) || user_access('use ipe with page manager', $account);
+}

+ 4 - 4
sites/all/modules/contrib/panels/panels/plugins/styles/stylizer.inc

@@ -194,7 +194,7 @@ function panels_stylizer_stylizer_style_settings_form($style_settings, $display,
  * Allow on-the-fly creation of styles in panes.
  */
 function panels_stylizer_pane_add_style(&$renderer, $plugin, &$conf, $type, $pid, $step = NULL) {
-  if (!user_access('administer panels styles')) {
+  if (!user_access("administer panels $type styles")) {
     return;
   }
 
@@ -275,7 +275,7 @@ function panels_stylizer_pane_add_style(&$renderer, $plugin, &$conf, $type, $pid
  * preconfigured style.
  */
 function panels_stylizer_edit_pane_style_form(&$form, &$form_state) {
-  if (!user_access('administer panels styles') || !module_exists('stylizer')) {
+  if (!user_access('administer panels pane styles') || !module_exists('stylizer')) {
     return;
   }
   ctools_include('dependent');
@@ -322,7 +322,7 @@ function panels_stylizer_edit_pane_style_form(&$form, &$form_state) {
  * Validate to see if we need to check the preconfigured values.
  */
 function panels_stylizer_edit_pane_style_form_validate(&$form, &$form_state) {
-  if (!user_access('administer panels styles')) {
+  if (!user_access('administer panels pane styles')) {
     return;
   }
 
@@ -356,7 +356,7 @@ function panels_stylizer_edit_pane_style_form_validate(&$form, &$form_state) {
  * Store the preconfigured values.
  */
 function panels_stylizer_edit_pane_style_form_submit(&$form, &$form_state) {
-  if (!user_access('administer panels styles')) {
+  if (!user_access('administer panels pane styles')) {
     return;
   }
 

+ 33 - 24
sites/all/modules/contrib/panels/panels/plugins/task_handlers/panel_context.inc

@@ -219,23 +219,23 @@ function panels_panel_context_tab_operation($handler, $contexts, $args) {
  *   for the handler and pla
  */
 function &panels_panel_context_get_display(&$handler) {
-  if (isset($handler->conf['display'])) {
-    return $handler->conf['display'];
-  }
-
-  if (isset($handler->conf['did'])) {
-    $handler->conf['display'] = panels_load_display($handler->conf['did']);
+  if (!isset($handler->conf['display'])) {
+    if (isset($handler->conf['did'])) {
+      $handler->conf['display'] = panels_load_display($handler->conf['did']);
+    }
 
-    // Check for a valid display. If no valid display can be loaded, something
-    // is wrong and we'll create a new one.
-    if (!empty($handler->conf['display'])) {
-      return $handler->conf['display'];
+    // Check for again for a valid display. If no valid display could be loaded,
+    // something is wrong and we'll create a new one.
+    if (empty($handler->conf['display'])) {
+      $handler->conf['display'] = panels_new_display();
     }
   }
 
-  $handler->conf['display'] = panels_new_display();
+  $display =& $handler->conf['display'];
+  $display->storage_type = 'page_manager';
+  $display->storage_id = !empty($handler->name) ? $handler->name : 'new';
 
-  return $handler->conf['display'];
+  return $display;
 }
 
 /**
@@ -286,7 +286,8 @@ function panels_panel_context_render($handler, $base_contexts, $args, $test = TR
 
   $display->context = $contexts;
   $display->args = $args;
-  $display->css_id = $handler->conf['css_id'];
+  $page_css_id = ctools_context_keyword_substitute($handler->conf['css_id'], array(), $contexts);
+  $display->css_id = check_plain($page_css_id);
   $task_name = page_manager_make_task_name($handler->task, $handler->subtask);
 
   $display->cache_key = panels_panel_context_cache_key($task_name, $handler->name, $args);
@@ -297,7 +298,9 @@ function panels_panel_context_render($handler, $base_contexts, $args, $test = TR
     $css_id = 'panel_context:' . $handler->name;
     $filename = ctools_css_retrieve($css_id);
     if (!$filename) {
-      $filename = ctools_css_store($css_id, $handler->conf['css']);
+      // Add keywords from context
+      $css = ctools_context_keyword_substitute($handler->conf['css'], array(), $contexts, array('css safe' => TRUE));
+      $filename = ctools_css_store($css_id, $css);
     }
     drupal_add_css($filename);
   }
@@ -318,19 +321,21 @@ function panels_panel_context_render($handler, $base_contexts, $args, $test = TR
   $panel_body_css = &drupal_static('panel_body_css');
 
   if (isset($handler->conf['body_classes_to_remove'])) {
+    $classes = ctools_context_keyword_substitute($handler->conf['body_classes_to_remove'], array(), $contexts, array('css safe' => TRUE));
     if (!isset($panel_body_css['body_classes_to_remove'])) {
-      $panel_body_css['body_classes_to_remove'] = $handler->conf['body_classes_to_remove'];
+      $panel_body_css['body_classes_to_remove'] = check_plain($classes);
     }
     else{
-      $panel_body_css['body_classes_to_remove'] .= ' ' . $handler->conf['body_classes_to_remove'];
+      $panel_body_css['body_classes_to_remove'] .= ' ' . check_plain($classes);
     }
   }
   if (isset($handler->conf['body_classes_to_add'])) {
+    $classes = ctools_context_keyword_substitute($handler->conf['body_classes_to_add'], array(), $contexts, array('css safe' => TRUE));
     if (!isset($panel_body_css['body_classes_to_add'])) {
-      $panel_body_css['body_classes_to_add'] = $handler->conf['body_classes_to_add'];
+    $panel_body_css['body_classes_to_add'] = check_plain($classes);
     }
     else {
-      $panel_body_css['body_classes_to_add'] .= ' '. $handler->conf['body_classes_to_add'];
+      $panel_body_css['body_classes_to_add'] .= ' '. check_plain($classes);
     }
   }
 
@@ -366,8 +371,8 @@ function panels_panel_context_save(&$handler, $update) {
   ctools_include('css');
   ctools_css_clear('panel_context:' . $handler->name);
 
-  if (isset($page->conf['temp_layout'])) {
-    unset($page->conf['temp_layout']);
+  if (isset($handler->conf['temp_layout'])) {
+    unset($handler->conf['temp_layout']);
   }
 }
 
@@ -738,6 +743,10 @@ function panels_panel_context_edit_content_validate(&$form, &$form_state) {
 }
 
 function panels_panel_context_edit_content_submit(&$form, &$form_state) {
+  // Update the storage_id if this is a new variant before saving.
+  if ($form_state['display']->storage_id == 'new') {
+    $form_state['display']->storage_id = $form_state['handler_id'];
+  }
   panels_edit_display_form_submit($form, $form_state);
   $handler = &$form_state['handler'];
 
@@ -775,7 +784,7 @@ function panels_panel_context_edit_settings($form, &$form_state) {
     '#size' => 128,
     '#default_value' => empty($conf['body_classes_to_remove']) ? '' : $conf['body_classes_to_remove'],
     '#title' => t('Remove body CSS classes'),
-    '#description' => t('The CSS classes to remove from the body element of this page. Separated by a space. For example: no-sidebars one-sidebar sidebar-first sidebar-second two-sidebars.'),
+    '#description' => t('The CSS classes to remove from the body element of this page. Separated by a space. For example: no-sidebars one-sidebar sidebar-first sidebar-second two-sidebars. Keywords from context are allowed.'),
   );
 
   $form['conf']['body_classes_to_add'] = array(
@@ -783,7 +792,7 @@ function panels_panel_context_edit_settings($form, &$form_state) {
     '#size' => 128,
     '#default_value' => empty($conf['body_classes_to_add']) ? '' : $conf['body_classes_to_add'],
     '#title' => t('Add body CSS classes'),
-    '#description' => t('The CSS classes to add to the body element of this page. Separated by a space. For example: no-sidebars one-sidebar sidebar-first sidebar-second two-sidebars.'),
+    '#description' => t('The CSS classes to add to the body element of this page. Separated by a space. For example: no-sidebars one-sidebar sidebar-first sidebar-second two-sidebars. Keywords from context are allowed.'),
   );
 
   ctools_include('plugins', 'panels');
@@ -823,13 +832,13 @@ function panels_panel_context_edit_settings($form, &$form_state) {
     '#size' => 35,
     '#default_value' => $conf['css_id'],
     '#title' => t('CSS ID'),
-    '#description' => t('The CSS ID to apply to this page'),
+    '#description' => t('The CSS ID to apply to this page. Keywords from context are allowed.'),
   );
 
   $form['conf']['css'] = array(
     '#type' => 'textarea',
     '#title' => t('CSS code'),
-    '#description' => t('Enter well-formed CSS code here; this code will be embedded into the page, and should only be used for minor adjustments; it is usually better to try to put CSS for the page into the theme if possible. This CSS will be filtered for safety so some CSS may not work.'),
+    '#description' => t('Enter well-formed CSS code here; this code will be embedded into the page, and should only be used for minor adjustments; it is usually better to try to put CSS for the page into the theme if possible. This CSS will be filtered for safety so some CSS may not work. Keywords from context are allowed.'),
     '#default_value' => $conf['css'],
   );
 

+ 6 - 0
sites/all/modules/contrib/panels/panels/templates/panels-add-content-modal.tpl.php

@@ -26,6 +26,12 @@
 
   <?php if (!empty($columns)): ?>
   <div class="panels-section-columns">
+    <div class="panels-section-columns-quickfilter container-inline">
+      <label for="quick-filter"><?php print t('Quick-Filter'); ?> </label> <input type="text" name="quickfilter" class="form-text" />
+      <script type="text/javascript">
+        (function ($) { Drupal.Panels.AddContentModalQuickFilter(); })(jQuery);
+      </script>
+    </div>
     <?php foreach ($columns as $column_id => $column): ?>
       <div class="panels-section-column panels-section-column-<?php print $column_id; ?> ">
         <div class="inside">

+ 103 - 0
sites/all/modules/contrib/panels/panels/tests/PanelsEntityViewWebTestCase.test

@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Some rudimentary tests for entity handling, will be overridden by others.
+ */
+
+abstract class PanelsEntityViewWebTestCase extends DrupalWebTestCase {
+
+  /**
+   * Admin user.
+   *
+   * @var \StdClass
+   */
+  protected $adminUser;
+
+  /**
+   * The task handler used for managing a specific entity type.
+   *
+   * @var \StdClass
+   */
+  protected $view_name = 'node_view';
+
+  /**
+   * The label used for this entity's task handler.
+   *
+   * @var \StdClass
+   */
+  protected $view_label = 'Node template';
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'panels';
+    $modules[] = 'page_manager';
+
+    parent::setUp($modules);
+
+    $permissions = array(
+      // Allow the user to access the Page Manager admin page.
+      'use page manager',
+
+      // Basic permissions for the module.
+      'use panels dashboard',
+
+      // General admin access.
+      'access administration pages',
+    );
+
+    // Reset the static variable used to identify permissions, otherwise it's
+    // possible the permissions check in drupalCreateUser will fail.
+    $this->checkPermissions(array(), TRUE);
+    cache_clear_all();
+
+    // Create the admin user.
+    $this->adminUser = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Ensure that the {ENTITY}_view toggle works correctly.
+   */
+  function testToggle() {
+    // Log in as the admin user.
+    $this->drupalLogin($this->adminUser);
+
+    // Load the Panels dashboard.
+    $this->drupalGet('admin/structure/panels');
+    $this->assertResponse(200, 'Loaded the main Panels admin page.');
+
+    // Confirm that the Node View task handler is disabled.
+    $this->assertText(t($this->view_label));
+    $this->assertLinkByHref(url('admin/structure/pages/edit/' . $this->view_name, array('absolute' => FALSE)));
+    $xpath = $this->xpath("//tr[contains(@class,:tr)]/td/div/div/ul/li[contains(@class,:li)]/a", array(':tr' => 'page-task-' . $this->view_name, ':li' => 'first'));
+    $this->assertEqual($xpath[0][0], t('Enable'));
+
+    // Set the Node View handler to "off".
+    variable_set('page_manager_' . $this->view_name . '_disabled', TRUE);
+
+    // Load the Panels dashboard again.
+    $this->drupalGet('admin/structure/panels');
+    $this->assertResponse(200, 'Loaded the main Panels admin page.');
+
+    // Confirm that the Node View task handler is still disabled.
+    $this->assertText(t($this->view_label));
+    $this->assertNoLinkByHref(url('admin/structure/pages/nojs/disable/' . $this->view_name, array('absolute' => FALSE)));
+    $xpath = $this->xpath("//tr[contains(@class,:tr)]/td/div/div/ul/li[contains(@class,:li)]/a", array(':tr' => 'page-task-' . $this->view_name, ':li' => 'first'));
+    $this->assertEqual($xpath[0][0], t('Enable'));
+
+    // Set the Node View handler to "on".
+    variable_set('page_manager_' . $this->view_name . '_disabled', FALSE);
+
+    // Load the Panels dashboard again.
+    $this->drupalGet('admin/structure/panels');
+    $this->assertResponse(200, 'Loaded the main Panels admin page.');
+
+    // Confirm that the Node View task handler is now enabled.
+    $this->assertLinkByHref(url('admin/structure/pages/nojs/disable/' . $this->view_name, array('absolute' => FALSE)));
+    $xpath = $this->xpath("//tr[contains(@class,:tr)]/td/div/div/ul/li[contains(@class,:li)]/a", array(':tr' => 'page-task-' . $this->view_name, ':li' => 'last'));
+    $this->assertEqual($xpath[0][0], t('Disable'));
+  }
+
+}

+ 31 - 0
sites/all/modules/contrib/panels/panels/tests/PanelsNodeViewWebTestCase.test

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Some rudimentary tests for the node_view handler.
+ */
+
+class PanelsNodeViewWebTestCase extends PanelsEntityViewWebTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Panels node_view tests',
+      'description' => 'Test the standard node_view task handler.',
+      'group' => 'Panels',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_name = 'node_view';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_label = 'Node template';
+
+}

+ 31 - 0
sites/all/modules/contrib/panels/panels/tests/PanelsTermViewWebTestCase.test

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Some rudimentary tests for the term_view handler.
+ */
+
+class PanelsTermViewWebTestCase extends PanelsEntityViewWebTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Panels term_view tests',
+      'description' => 'Test the standard term_view task handler.',
+      'group' => 'Panels',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_name = 'term_view';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_label = 'Taxonomy term template';
+
+}

+ 31 - 0
sites/all/modules/contrib/panels/panels/tests/PanelsUserViewWebTestCase.test

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Some rudimentary tests for the user_view handler.
+ */
+
+class PanelsUserViewWebTestCase extends PanelsEntityViewWebTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Panels user_view tests',
+      'description' => 'Test the standard user_view task handler.',
+      'group' => 'Panels',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_name = 'user_view';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $view_label = 'Users';
+
+}

+ 49 - 2
sites/all/modules/contrib/search/search_api/CHANGELOG.txt

@@ -1,5 +1,52 @@
-Search API 1.x, dev (xxxx-xx-xx):
----------------------------------
+Search API 1.20 (2016-07-21):
+-----------------------------
+- #2731103 by drunken monkey: Fixed the default value for the taxonomy term
+  filter "multiple" setting.
+- #1818572 by morningtime, drunken monkey, lodey, guillaumev: Added pretty
+  paths support to the Views facets block.
+- #2753441 by Johnny vd Laar: Fixed translated field names in
+  language-independent cache.
+
+Search API 1.19 (2016-07-05):
+-----------------------------
+- #2724687 by StefanPr, drunken monkey: Fixed failed sanitization of NULL field
+  values.
+- #2744189 by nikolabintev, drunken monkey: Fixed highlighting for single-word
+  fields.
+- #2744995 by John Cook, drunken monkey: Fixed search views without pager.
+- #2742053 by tunic: Fixed change notification on node access records change.
+- #2733447 by jsacksick: Fixed translatability of our Views taxonomy term
+  filter.
+- #2720465 by drunken monkey: Fixed bundle filter's handling of entity types
+  with no bundles on multi-type indexes.
+- #2710893 by alan-ps, drunken monkey: Fixed creation of comment indexes when
+  no nodes exist.
+- #2707039 by alan-ps: Fixed indexes of flag entities with "bundles" setting.
+- #2700879 by drunken monkey: Fixed breadcrumbs on index tabs.
+- #1889940 by cspurk, Yaron Tal: Fixed "HTML filter" processor to recognize all
+  valid HTML tags.
+- #2700011 by drunken monkey: Fixed compatibility issues of facets from
+  different indexes.
+- #2665970 by andrei.colesnic, drunken monkey: Added "Limit list to selected
+  items" exposed option support for Views taxonomy term filters.
+- #2703675 by drunken monkey, heykarthikwithu: Fixed accidental assumption that
+  all facets are taxonomy terms.
+- #2419853 by drunken monkey: Fixed HTML filter leaves escaped entities in
+  field values sometimes.
+
+Search API 1.18 (2016-04-20):
+-----------------------------
+- Various security fixes – see https://www.drupal.org/node/2710063.
+- #2693425 by jojyja: Fixed a typo in search_api.info.
+
+Search API 1.17 (2016-03-14):
+-----------------------------
+- #2665586 by recrit, drunken monkey: Fixed parsing of invalid date facet
+  filters.
+- #2677900 by stefan.r, drunken monkey: Added the possibility to change date
+  facet formats.
+- #2678856 by stefan.r, drunken monkey: Fixed date facets showing wrong month
+  on certain days.
 - #2667872 by Les Lim: Added "0" to field boost options.
 - #2654328 by drunken monkey, donquixote: Fixed use of "<" and ">" for open
   facet ranges.

+ 10 - 3
sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/adapter.inc

@@ -61,6 +61,10 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
   public function initActiveFilters($query) {
     $search_id = $query->getOption('search id');
     $index_id = $this->info['instance'];
+    // Only act on queries from the right index.
+    if ($index_id != $query->getIndex()->machine_name) {
+      return;
+    }
     $facets = facetapi_get_enabled_facets($this->info['name']);
     $this->fields = array();
 
@@ -83,13 +87,16 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
 
       if (array_search($search_id, $facet_search_ids) === FALSE) {
         if (!$default_true) {
-          continue; // We are only to show facets for explicitly named search ids.
+          // We are only to show facets for explicitly named search ids.
+          continue;
         }
       }
       elseif ($default_true) {
-        continue; // The 'facet_search_ids' in the settings are to be excluded.
+        // The 'facet_search_ids' in the settings are to be excluded.
+        continue;
       }
-      $active[$facet['name']] = $search_id;
+      $facet_key = $facet['name'] . '@' . $this->getSearcher();
+      $active[$facet_key] = $search_id;
       $this->fields[$facet['name']] = array(
         'field'             => $facet['field'],
         'limit'             => $options['hard_limit'],

Some files were not shown because too many files changed in this diff