Browse Source

uppdated modules

Bachir Soussi Chiadmi 7 years ago
parent
commit
8416e3eea1
100 changed files with 4810 additions and 1528 deletions
  1. 5 0
      fix-perms
  2. 3 0
      sites/all/modules/contrib/admin/admin_menu_source/CHANGELOG.txt
  3. 3 4
      sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.inc
  4. 5 3
      sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.info
  5. 20 12
      sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.module
  6. 0 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/LICENSE.txt
  7. 192 71
      sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.css
  8. 3 3
      sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.info
  9. 10 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.install
  10. 86 2
      sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.module
  11. 35 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_menu_settings.inc
  12. 2 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/credits.txt
  13. 6 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/circledown.svg
  14. 14 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/home.svg
  15. 9 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/house.svg
  16. 11 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/menu-arrows.svg
  17. 50 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/menu.svg
  18. 27 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/images/tasks.svg
  19. 12 16
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/adminimal_admin_menu.js
  20. 1 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/jquery.min.js
  21. 19 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/MIT-LICENSE.txt
  22. 90 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/README.md
  23. 432 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/jquery-no-conflict.slicknav.js
  24. 432 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/jquery.slicknav.js
  25. 256 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/slicknav.css
  26. 18 0
      sites/all/modules/contrib/admin/adminimal_admin_menu/updates/update_7100.php
  27. 0 0
      sites/all/modules/contrib/admin/content_type_extras/LICENSE.txt
  28. 3 3
      sites/all/modules/contrib/admin/content_type_extras/content_type_extras.info
  29. 87 40
      sites/all/modules/contrib/admin/content_type_extras/content_type_extras.module
  30. 52 7
      sites/all/modules/contrib/admin/content_type_extras/includes/content_type_extras.admin.inc
  31. 131 60
      sites/all/modules/contrib/admin/content_type_extras/includes/content_type_extras.node_type_form.inc
  32. 44 9
      sites/all/modules/contrib/admin/content_type_extras/js/content_type_extras.cancel_button.js
  33. 3 3
      sites/all/modules/contrib/admin/context/context.info
  34. 1 1
      sites/all/modules/contrib/admin/context/context.install
  35. 55 5
      sites/all/modules/contrib/admin/context/context.module
  36. 2 2
      sites/all/modules/contrib/admin/context/context.plugins.inc
  37. 3 3
      sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info
  38. 3 3
      sites/all/modules/contrib/admin/context/context_ui/context_ui.info
  39. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui.js
  40. 8 12
      sites/all/modules/contrib/admin/context/context_ui/context_ui.module
  41. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js
  42. 11 11
      sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php
  43. 1 1
      sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test
  44. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc
  45. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc
  46. 9 5
      sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc
  47. 4 5
      sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js
  48. 1 0
      sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc
  49. 3 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc
  50. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc
  51. 0 22
      sites/all/modules/contrib/admin/context/tests/context.conditions.test
  52. 3 4
      sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc
  53. 100 33
      sites/all/modules/contrib/admin/features/features.admin.inc
  54. 24 3
      sites/all/modules/contrib/admin/features/features.api.php
  55. 150 21
      sites/all/modules/contrib/admin/features/features.drush.inc
  56. 169 14
      sites/all/modules/contrib/admin/features/features.export.inc
  57. 7 3
      sites/all/modules/contrib/admin/features/features.info
  58. 27 4
      sites/all/modules/contrib/admin/features/features.install
  59. 7 5
      sites/all/modules/contrib/admin/features/features.js
  60. 111 19
      sites/all/modules/contrib/admin/features/features.module
  61. 115 0
      sites/all/modules/contrib/admin/features/includes/features.contact.inc
  62. 56 9
      sites/all/modules/contrib/admin/features/includes/features.field.inc
  63. 20 11
      sites/all/modules/contrib/admin/features/includes/features.image.inc
  64. 3 2
      sites/all/modules/contrib/admin/features/includes/features.locale.inc
  65. 3 3
      sites/all/modules/contrib/admin/features/includes/features.menu.inc
  66. 15 4
      sites/all/modules/contrib/admin/features/includes/features.node.inc
  67. 41 1
      sites/all/modules/contrib/admin/features/tests/features.test
  68. 0 8
      sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc
  69. 3 3
      sites/all/modules/contrib/admin/features/tests/features_test/features_test.info
  70. 3 3
      sites/all/modules/contrib/admin/features_extra/fe_block.info
  71. 14 0
      sites/all/modules/contrib/admin/features_extra/fe_date.info
  72. 345 0
      sites/all/modules/contrib/admin/features_extra/fe_date.module
  73. 3 3
      sites/all/modules/contrib/admin/features_extra/fe_nodequeue.info
  74. 3 3
      sites/all/modules/contrib/admin/features_extra/fe_profile.info
  75. 3 3
      sites/all/modules/contrib/admin/features_extra/tests/features_extra_test.info
  76. 0 0
      sites/all/modules/contrib/admin/field_permissions/CHANGELOG.txt
  77. 0 0
      sites/all/modules/contrib/admin/field_permissions/LICENSE.txt
  78. 0 0
      sites/all/modules/contrib/admin/field_permissions/README.txt
  79. 10 12
      sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.css
  80. 81 50
      sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.inc
  81. 59 48
      sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.js
  82. 85 0
      sites/all/modules/contrib/admin/field_permissions/field_permissions.api.php
  83. 3 5
      sites/all/modules/contrib/admin/field_permissions/field_permissions.info
  84. 22 0
      sites/all/modules/contrib/admin/field_permissions/field_permissions.install
  85. 85 1
      sites/all/modules/contrib/admin/field_permissions/field_permissions.module
  86. 6 6
      sites/all/modules/contrib/admin/field_permissions/field_permissions.test
  87. 0 0
      sites/all/modules/contrib/admin/field_permissions/images/field_permissions.status-off.png
  88. 0 0
      sites/all/modules/contrib/admin/field_permissions/images/field_permissions.status-on.png
  89. 3 3
      sites/all/modules/contrib/admin/fpa/fpa.info
  90. 1 1
      sites/all/modules/contrib/admin/fpa/fpa.theme.inc
  91. 13 0
      sites/all/modules/contrib/admin/linkit/better-autocomplete/better-autocomplete.css
  92. 804 789
      sites/all/modules/contrib/admin/linkit/better-autocomplete/jquery.better-autocomplete.js
  93. 7 1
      sites/all/modules/contrib/admin/linkit/css/linkit.css
  94. 1 1
      sites/all/modules/contrib/admin/linkit/editors/ckeditor/linkitDialog.js
  95. 26 10
      sites/all/modules/contrib/admin/linkit/editors/ckeditor/plugin.js
  96. 12 2
      sites/all/modules/contrib/admin/linkit/editors/tinymce/editor_plugin.js
  97. 5 6
      sites/all/modules/contrib/admin/linkit/js/linkit.dashboard.js
  98. 136 121
      sites/all/modules/contrib/admin/linkit/js/linkit.field.js
  99. 37 1
      sites/all/modules/contrib/admin/linkit/js/linkit.js
  100. 20 1
      sites/all/modules/contrib/admin/linkit/linkit.api.php

+ 5 - 0
fix-perms

@@ -2,3 +2,8 @@
 
 
 sudo chown -R $USER:http sites/all/modules/features/
 sudo chown -R $USER:http sites/all/modules/features/
 sudo chmod -R g+w sites/all/modules/features/
 sudo chmod -R g+w sites/all/modules/features/
+
+sudo chown -R $USER:http sites/default/files
+sudo chown -R $USER:http sites/default/private
+sudo chmod -R g+w sites/default/files
+sudo chmod -R g+w sites/default/private

+ 3 - 0
sites/all/modules/contrib/admin/admin_menu_source/CHANGELOG.txt

@@ -0,0 +1,3 @@
+Admin Menu Source 7.x-1.1, 2016-04-01
+-------------------
+- Issue #2634278 by Tommy Cox: Missed dependency of Menu module

+ 3 - 4
sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.inc

@@ -1,5 +1,4 @@
 <?php
 <?php
-
 /**
 /**
  * @file
  * @file
  * Menu callbacks for admin_menu_source module
  * Menu callbacks for admin_menu_source module
@@ -27,13 +26,13 @@ function admin_menu_source_settings() {
     );
     );
   }
   }
   
   
-  //add a custom submit handler
+  // Add a custom submit handler.
   $form['#submit'][] = 'admin_menu_source_settings_submit';
   $form['#submit'][] = 'admin_menu_source_settings_submit';
   
   
   return system_settings_form($form);
   return system_settings_form($form);
 }
 }
 
 
 function admin_menu_source_settings_submit() {
 function admin_menu_source_settings_submit() {
-  //flush admin_menu's cache
+  // Flush admin_menu's cache.
   admin_menu_flush_caches();
   admin_menu_flush_caches();
-}
+}

+ 5 - 3
sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.info

@@ -1,12 +1,14 @@
 name = Administration Menu Source
 name = Administration Menu Source
 description = Use a different menu as the Administration Menu.
 description = Use a different menu as the Administration Menu.
+dependencies[] = menu
 dependencies[] = admin_menu
 dependencies[] = admin_menu
 package = Administration
 package = Administration
 core = 7.x
 core = 7.x
 configure = admin/config/administration/admin_menu/source
 configure = admin/config/administration/admin_menu/source
-; Information added by drupal.org packaging script on 2012-04-03
-version = "7.x-1.0"
+
+; Information added by Drupal.org packaging script on 2016-04-01
+version = "7.x-1.1"
 core = "7.x"
 core = "7.x"
 project = "admin_menu_source"
 project = "admin_menu_source"
-datestamp = "1333434040"
+datestamp = "1459490342"
 
 

+ 20 - 12
sites/all/modules/contrib/admin/admin_menu_source/admin_menu_source.module

@@ -1,5 +1,4 @@
 <?php
 <?php
-
 /**
 /**
  * @file
  * @file
  * Use a different menu as the Administration Menu.
  * Use a different menu as the Administration Menu.
@@ -56,14 +55,23 @@ function admin_menu_source_theme() {
 function admin_menu_source_admin_menu_output_alter(&$content) {
 function admin_menu_source_admin_menu_output_alter(&$content) {
   global $user;
   global $user;
   
   
-  $rid = key(array_reverse($user->roles, TRUE));
-  $source_menu = _admin_menu_source_get_role_menu($rid);
-  
-  if (!empty($source_menu)) {
-    $content['menu'] = admin_menu_links_menu(admin_menu_tree($source_menu));
-    $content['menu']['#theme'] = 'admin_menu_links';
-    $content['menu']['#weight'] = 0;
-    $content['menu']['#sorted'] = TRUE;
+  // $rid = key(array_reverse($user->roles, TRUE));
+
+  // Find the user role rid.
+  $roles_ids = array_keys(user_roles(TRUE, 'access administration menu'));
+  $user_roles_ids = array_keys($user->roles);
+  $user_roles = array_reverse(array_intersect($roles_ids, $user_roles_ids));
+
+  if (count($user_roles)) {
+    $rid = $user_roles[0];
+    $source_menu = _admin_menu_source_get_role_menu($rid);
+
+    if (!empty($source_menu)) {
+      $content['menu'] = admin_menu_links_menu(admin_menu_tree($source_menu));
+      $content['menu']['#theme'] = 'admin_menu_links';
+      $content['menu']['#weight'] = 0;
+      $content['menu']['#sorted'] = TRUE;
+    }
   }
   }
 }
 }
 
 
@@ -97,20 +105,20 @@ function theme_admin_menu_source_settings_form($variables) {
 }
 }
 
 
 /**
 /**
- * Helper function to get settings for admin_menu_source
+ * Helper function to get settings for admin_menu_source.
  */
  */
 function _admin_menu_source_get_settings() {
 function _admin_menu_source_get_settings() {
   return variable_get('admin_menu_source_settings', array());
   return variable_get('admin_menu_source_settings', array());
 }
 }
 
 
 /**
 /**
- * Helper function to get source menu per role
+ * Helper function to get source menu per role.
  *
  *
  * @param $rid
  * @param $rid
  *  the user role id
  *  the user role id
  */
  */
 function _admin_menu_source_get_role_menu($rid) {
 function _admin_menu_source_get_role_menu($rid) {
-  //load the settings
+  // Load the settings.
   $settings = _admin_menu_source_get_settings();
   $settings = _admin_menu_source_get_settings();
   
   
   return isset($settings[$rid]['source']) ? $settings[$rid]['source'] : '';
   return isset($settings[$rid]['source']) ? $settings[$rid]['source'] : '';

+ 0 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/LICENSE.txt


+ 192 - 71
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.css

@@ -1,16 +1,19 @@
 /* Import the Open Sans webfont from Google CDN */
 /* Import the Open Sans webfont from Google CDN */
+
 @font-face {
 @font-face {
   font-family: 'Open Sans';
   font-family: 'Open Sans';
   font-style: normal;
   font-style: normal;
   font-weight: 300;
   font-weight: 300;
   src: local('Open Sans Light'), local('OpenSans-Light'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTRsxEYwM7FgeyaSgU71cLG0.woff) format('woff');
   src: local('Open Sans Light'), local('OpenSans-Light'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTRsxEYwM7FgeyaSgU71cLG0.woff) format('woff');
 }
 }
+
 @font-face {
 @font-face {
   font-family: 'Open Sans';
   font-family: 'Open Sans';
   font-style: normal;
   font-style: normal;
   font-weight: 400;
   font-weight: 400;
   src: local('Open Sans'), local('OpenSans'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/uYKcPVoh6c5R0NpdEY5A-Q.woff) format('woff');
   src: local('Open Sans'), local('OpenSans'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/uYKcPVoh6c5R0NpdEY5A-Q.woff) format('woff');
 }
 }
+
 @font-face {
 @font-face {
   font-family: 'Open Sans';
   font-family: 'Open Sans';
   font-style: normal;
   font-style: normal;
@@ -18,32 +21,86 @@
   src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/MTP_ySUJH_bn48VBG8sNShsxEYwM7FgeyaSgU71cLG0.woff) format('woff');
   src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url(https://themes.googleusercontent.com/static/fonts/opensans/v6/MTP_ySUJH_bn48VBG8sNShsxEYwM7FgeyaSgU71cLG0.woff) format('woff');
 }
 }
 
 
+
 /* Blink Animation */
 /* Blink Animation */
+
 @keyframes blink {
 @keyframes blink {
-    0% {background-color: #f69231;}
-    50% {background-color: #d94f13;}
-    99% {background-color: #f69231;}
+  0% {
+    background-color: #f69231;
+    color: #fff;
+  }
+  50% {
+    background-color: #d94f13;
+    color: #fff;
+  }
+  100% {
+    background-color: #f69231;
+    color: #fff;
+  }
 }
 }
 
 
 @-moz-keyframes blink {
 @-moz-keyframes blink {
-    0% {background-color: #f69231;}
-    50% {background-color: #d94f13;}
-    99% {background-color: #f69231;}
+  0% {
+    background-color: #f69231;
+    color: #fff;
+  }
+  50% {
+    background-color: #d94f13;
+    color: #fff;
+  }
+  100% {
+    background-color: #f69231;
+    color: #fff;
+  }
 }
 }
 
 
 @-webkit-keyframes blink {
 @-webkit-keyframes blink {
-    0% {background-color: #f69231;}
-    50% {background-color: #d94f13;}
-    99% {background-color: #f69231;}
+  0% {
+    background-color: #f69231;
+    color: #fff;
+  }
+  50% {
+    background-color: #d94f13;
+    color: #fff;
+  }
+  100% {
+    background-color: #f69231;
+    color: #fff;
+  }
 }
 }
 
 
 @-o-keyframes blink {
 @-o-keyframes blink {
-    0% {background-color: #f69231;}
-    50% {background-color: #d94f13;}
-    99% {background-color: #f69231;}
+  0% {
+    background-color: #f69231;
+    color: #fff;
+  }
+  50% {
+    background-color: #d94f13;
+    color: #fff;
+  }
+  100% {
+    background-color: #f69231;
+    color: #fff;
+  }
+}
+
+body.admin-menu.adminimal-menu #admin-menu *::-moz-focus-inner {
+  border: 0;
+}
+
+body.admin-menu.adminimal-menu {
+  margin-top: 0px !important;
+}
+
+body.admin-menu.adminimal-menu:before {
+  content: "";
+  display: block;
+  width: 100%;
+  height: 29px;
+  top: 0;
+  left: 0;
 }
 }
 
 
-/* Admin menu */
 body.adminimal-menu div#admin-menu {
 body.adminimal-menu div#admin-menu {
   background: #333;
   background: #333;
   background: none repeat scroll 0 0 #202020;
   background: none repeat scroll 0 0 #202020;
@@ -56,15 +113,11 @@ body.adminimal-menu div#admin-menu li.admin-menu-toolbar-home-menu {
 }
 }
 
 
 body.adminimal-menu div#admin-menu ul.dropdown span.admin-menu-home-icon {
 body.adminimal-menu div#admin-menu ul.dropdown span.admin-menu-home-icon {
-  background: url("images/home.png") no-repeat scroll 0 2px transparent;
+  background: url("images/home.svg") no-repeat scroll 0 0 transparent;
   display: block;
   display: block;
   margin: 5px 0 0;
   margin: 5px 0 0;
   padding: 0;
   padding: 0;
-  width: 13px;
-}
-
-body.adminimal-menu div#admin-menu ul.dropdown span.admin-menu-home-icon:hover {
-  background-position: 0px -86px;
+  width: 15px;
 }
 }
 
 
 body.adminimal-menu div#admin-menu a,
 body.adminimal-menu div#admin-menu a,
@@ -105,6 +158,10 @@ body.adminimal-menu #admin-menu .dropdown li.admin-menu-toolbar-category a {
   line-height: 20px;
   line-height: 20px;
 }
 }
 
 
+body.adminimal-menu #admin-menu .dropdown li.admin-menu-toolbar-category a:focus {
+  background: #0074BD;
+}
+
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li {
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li {
   opacity: 1;
   opacity: 1;
 }
 }
@@ -123,14 +180,15 @@ body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li.expandable {
 }
 }
 
 
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li.expandable a {
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li.expandable a {
-  background: transparent !important;
+  background: transparent;
   padding-right: 25px;
   padding-right: 25px;
-  width: 135px;
 }
 }
 
 
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li.expandable:hover {
 body.adminimal-menu #admin-menu #admin-menu-wrapper .dropdown li li.expandable:hover {
   background: url("images/square.png") no-repeat scroll 145px -83px #0074bd !important;
   background: url("images/square.png") no-repeat scroll 145px -83px #0074bd !important;
 }
 }
+
+
 /* Shortcuts bar */
 /* Shortcuts bar */
 
 
 body.adminimal-menu div.toolbar-shortcuts {
 body.adminimal-menu div.toolbar-shortcuts {
@@ -146,18 +204,37 @@ body.adminimal-menu div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.me
   float: left;
   float: left;
   list-style: none outside none;
   list-style: none outside none;
   padding: 0;
   padding: 0;
+  margin: 0;
 }
 }
 
 
 body.adminimal-menu div.shortcut-toolbar div#toolbar a#edit-shortcuts {
 body.adminimal-menu div.shortcut-toolbar div#toolbar a#edit-shortcuts {
   float: right;
   float: right;
   line-height: 18px;
   line-height: 18px;
   padding: 5px 10px;
   padding: 5px 10px;
+  text-decoration: none;
 }
 }
 
 
 body.adminimal-menu div.shortcut-toolbar div#toolbar a#edit-shortcuts:hover {
 body.adminimal-menu div.shortcut-toolbar div#toolbar a#edit-shortcuts:hover {
   text-decoration: none;
   text-decoration: none;
 }
 }
 
 
+body.adminimal-menu div.shortcut-toolbar div#toolbar a#edit-shortcuts:focus {
+  background: #0074BD;
+}
+
+body.adminimal-menu #admin-menu .dropdown li ul {
+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
+}
+
+body.adminimal-menu #admin-menu .dropdown li ul {
+  border-top: 3px solid #0074BD;
+}
+
+body.adminimal-menu #admin-menu .admin-menu-toolbar-category.expandable .dropdown li ul,
+body.adminimal-menu #admin-menu .dropdown.admin-menu-search-results li ul {
+  border-top: none;
+}
+
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action a {
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action a {
   border: none !important;
   border: none !important;
 }
 }
@@ -167,7 +244,8 @@ body.adminimal-menu #admin-menu .dropdown li.admin-menu-action:first-child a {
   padding: 5px 10px;
   padding: 5px 10px;
 }
 }
 
 
-body.adminimal-menu #admin-menu .dropdown li.admin-menu-action:first-child a:hover {
+body.adminimal-menu #admin-menu .dropdown li.admin-menu-action:first-child a:hover,
+body.adminimal-menu #admin-menu .dropdown li.admin-menu-action:first-child a:focus {
   background: #b73939;
   background: #b73939;
 }
 }
 
 
@@ -176,7 +254,7 @@ body.adminimal-menu #admin-menu .dropdown li.admin-menu-action.admin-menu-users
 }
 }
 
 
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action.admin-menu-users a:hover {
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action.admin-menu-users a:hover {
-    background: #0074BD;
+  background: #0074BD;
 }
 }
 
 
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action.admin-menu-account a {
 body.adminimal-menu #admin-menu .dropdown li.admin-menu-action.admin-menu-account a {
@@ -204,107 +282,124 @@ body.adminimal-menu #admin-menu #admin-menu-wrapper div#toolbar div.toolbar-shor
 }
 }
 
 
 body.adminimal-menu #admin-menu .admin-menu-search input {
 body.adminimal-menu #admin-menu .admin-menu-search input {
-    background: none no-repeat scroll right center #444;
-    border: medium none;
-    border-radius: 0 0 0 0;
-    color: #999999;
-    font-size: 12px;
-    margin: 0 !important;
-    outline: medium none;
-    padding: 5px 22px 3px 8px;
-    width: 110px;
-    height: 20px;
+  background: none no-repeat scroll right center #444;
+  border: medium none;
+  border-radius: 0 0 0 0;
+  color: #999999;
+  font-size: 12px;
+  margin: 0 !important;
+  outline: medium none;
+  padding: 5px 22px 3px 8px;
+  width: 110px;
+  height: 20px;
 }
 }
 
 
-body.adminimal-menu #admin-menu .admin-menu-search input:focus, #admin-menu .admin-menu-search input:hover, #admin-menu .admin-menu-search input:active {
-    background: none no-repeat scroll right center #888;
-    color: #eee;
+body.adminimal-menu #admin-menu .admin-menu-search input:focus,
+#admin-menu .admin-menu-search input:hover,
+#admin-menu .admin-menu-search input:active {
+  background: none no-repeat scroll right center #888;
+  color: #eee;
 }
 }
 
 
 body.adminimal-menu li.admin-menu-search:hover {
 body.adminimal-menu li.admin-menu-search:hover {
-    background: transparent !important;
+  background: transparent !important;
 }
 }
 
 
-body.adminimal-menu #admin-menu li.highlight {
-  background-color: #ef6114 !important;
-  animation: blink 0.5s ease infinite;
-  -moz-animation: blink 0.5s ease infinite;
-  -webkit-animation: blink 0.5s ease infinite;
-  -o-animation: blink 0.5s ease infinite;
+body.adminimal-menu div#admin-menu li.highlight {
+  background-color: #ef6114;
+  animation: blink 1s ease both infinite;
+  -moz-animation: blink 1s ease both infinite;
+  -webkit-animation: blink 1s ease both infinite;
+  -o-animation: blink 1s ease both infinite;
+  color: #fff !important;
 }
 }
 
 
-body.adminimal-menu #admin-menu li.highlight > a {
-  border-color: #ef6114 !important;
+body.adminimal-menu #admin-menu li.highlight > a,
+body.adminimal-menu #admin-menu li.highlight > li {
+  border-color: #ef6114;
   color: #fff !important;
   color: #fff !important;
+  animation: blink 1s ease both infinite;
+  -moz-animation: blink 1s ease both infinite;
+  -webkit-animation: blink 1s ease both infinite;
+  -o-animation: blink 1s ease both infinite;
 }
 }
 
 
+
 /* Newline render mode */
 /* Newline render mode */
+
 body.adminimal-menu.admin-menu.menu-render-newline div#admin-menu div#admin-menu-wrapper div.shortcut-toolbar {
 body.adminimal-menu.admin-menu.menu-render-newline div#admin-menu div#admin-menu-wrapper div.shortcut-toolbar {
-    clear: both;
-    border-top: 1px solid #000;
+  clear: both;
+  border-top: 1px solid #000;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-newline {
 body.adminimal-menu.admin-menu.menu-render-newline {
-    margin-top: 56px !important;
+  margin-top: 56px !important;
 }
 }
 
 
-body.adminimal-menu.admin-menu.menu-render-newline #overlay-container, body.adminimal-menu.admin-menu.newline .overlay-modal-background, body.adminimal-menu.admin-menu.newline .overlay-element, body.adminimal-menu.admin-menu.newline #overlay {
-    padding-top: 30px !important;
+body.adminimal-menu.admin-menu.menu-render-newline #overlay-container,
+body.adminimal-menu.admin-menu.newline .overlay-modal-background,
+body.adminimal-menu.admin-menu.newline .overlay-element,
+body.adminimal-menu.admin-menu.newline #overlay {
+  padding-top: 30px !important;
 }
 }
 
 
+
 /* Collapsed render mode */
 /* Collapsed render mode */
+
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu {
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu {
-    position: absolute;
+  position: absolute;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li {
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li {
-    float: none;
-    display: none;
-    border: none;
+  float: none;
+  display: none;
+  border: none;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li.label {
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li.label {
-    display: block;
-    cursor: default;
+  display: block;
+  cursor: default;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li.label a:hover {
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu li.label a:hover {
-    background: url("images/shortcut.png") no-repeat scroll 7px 7px #202020 !important;
-    color: #EEEEEE !important;
+  background: url("images/shortcut.png") no-repeat scroll 7px 7px #202020 !important;
+  color: #EEEEEE !important;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu:hover li {
 body.adminimal-menu.admin-menu.menu-render-collapsed div.shortcut-toolbar div#toolbar div.toolbar-shortcuts ul.menu:hover li {
-    display: block;
-    background: #202020;
+  display: block;
+  background: #202020;
 }
 }
 
 
 
 
 /* Dropdown render mode */
 /* Dropdown render mode */
+
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu {
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu {
-    -webkit-appearance: none;
-    background: url("images/shortcut.png") no-repeat scroll 7px 7px #222222;
-    border: medium none;
-    color: #EEEEEE;
-    font-family: "Open Sans", "Segoe UI", "Helvetica", sans-serif;
-    font-size: 12px;
-    padding: 5px 4px 4px 24px;
+  -webkit-appearance: none;
+  background: url("images/shortcut.png") no-repeat scroll 7px 7px #222222;
+  border: medium none;
+  color: #EEEEEE;
+  font-family: "Open Sans", "Segoe UI", "Helvetica", sans-serif;
+  font-size: 12px;
+  padding: 5px 4px 4px 24px;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu option:hover {
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu option:hover {
-    cursor: pointer;
+  cursor: pointer;
 }
 }
 
 
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu option:first-child {
 body.adminimal-menu.admin-menu.menu-render-dropdown .toolbar-shortcuts select#shortcut-menu option:first-child {
-    display: none;
+  display: none;
 }
 }
 
 
 body.adminimal-menu #admin-menu .dropdown .admin-menu-tab {
 body.adminimal-menu #admin-menu .dropdown .admin-menu-tab {
-    background: #444;
-    padding-bottom: 2px;
+  background: #444;
+  padding-bottom: 2px;
 }
 }
 
 
 
 
 /* Environment indicator 2.x */
 /* Environment indicator 2.x */
+
 body.adminimal-menu #admin-menu #environment-indicator {
 body.adminimal-menu #admin-menu #environment-indicator {
   text-shadow: none;
   text-shadow: none;
 }
 }
@@ -317,3 +412,29 @@ body.adminimal-menu #admin-menu #environment-indicator .environment-indicator-na
   line-height: 18px;
   line-height: 18px;
   padding: 5px 10px;
   padding: 5px 10px;
 }
 }
+
+body.adminimal-menu #admin-menu #admin-menu-wrapper div#toolbar div.toolbar-shortcuts ul,
+body.adminimal-menu #admin-menu #admin-menu-wrapper div#toolbar div.toolbar-shortcuts ul li {
+  margin: 0;
+  padding: 0;
+}
+
+body.adminimal-menu #admin-menu .admin-menu-search input {
+  background: none no-repeat scroll right center #444;
+  border: medium none;
+  -webkit-border-radius: 0 0 0 0;
+  -moz-border-radius: 0 0 0 0;
+  border-radius: 0 0 0 0;
+  color: #999999;
+  font-size: 12px;
+  margin: 0 !important;
+  outline: medium none;
+  padding: 5px 22px 3px 8px;
+  width: 110px;
+  line-height: 20px;
+  -webkit-box-shadow: none;
+  -moz-box-shadow: none;
+  box-shadow: none;
+  height: 28px;
+  box-sizing: border-box;
+}

+ 3 - 3
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.info

@@ -4,9 +4,9 @@ package = Administration
 core = 7.x
 core = 7.x
 dependencies[] = admin_menu
 dependencies[] = admin_menu
 
 
-; Information added by Drupal.org packaging script on 2014-01-27
-version = "7.x-1.5"
+; Information added by Drupal.org packaging script on 2015-12-03
+version = "7.x-1.7"
 core = "7.x"
 core = "7.x"
 project = "adminimal_admin_menu"
 project = "adminimal_admin_menu"
-datestamp = "1390792706"
+datestamp = "1449182323"
 
 

+ 10 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.install

@@ -78,3 +78,13 @@ function adminimal_admin_menu_uninstall() {
   variable_del('adminimal_admin_menu_render');
   variable_del('adminimal_admin_menu_render');
 
 
 }
 }
+
+/**
+ * Clean up some files that are no longer needed.
+ */
+function adminimal_admin_menu_update_7100() {
+
+  // Include the update file.
+  include 'updates/update_7100.php';
+
+}

+ 86 - 2
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.module

@@ -31,10 +31,46 @@ function adminimal_admin_menu_page_build(&$page) {
     return;
     return;
   }
   }
   $path = drupal_get_path('module', 'adminimal_admin_menu');
   $path = drupal_get_path('module', 'adminimal_admin_menu');
+	$load_slicknav = variable_get('adminimal_admin_menu_slicknav', TRUE );
+	$load_jQuery = variable_get('adminimal_admin_menu_jquery', TRUE );
 
 
   // Attach the CSS and JavaScript assets.
   // Attach the CSS and JavaScript assets.
   drupal_add_css($path . '/adminimal_admin_menu.css');
   drupal_add_css($path . '/adminimal_admin_menu.css');
-  drupal_add_js($path . '/adminimal_admin_menu.js', 'file');
+
+	// Support for the Environment Indicator module.
+  if (module_exists('environment_indicator')) {
+  $environment_info = environment_indicator_get_active();
+	  drupal_add_css('
+      div.slicknav_menu a.slicknav_btn:after {
+      	content: "' . $environment_info['name']. '";
+      	margin-left: 1em;
+      	color: ' . $environment_info['text_color'] . ';
+      	font-size: 12px;
+      	background-color: ' . $environment_info['color'] . ';
+				padding: 2px 5px;
+			}
+			#admin-menu-wrapper {background: #222 !important;}
+			',
+      array(
+        'group' => CSS_DEFAULT,
+        'type' => 'inline',
+        'preprocess' => FALSE,
+        'weight' => '100',
+      ));
+	}
+
+	// Check if both slicknav and custom jQuery must be loaded.
+	if ($load_slicknav and $load_jQuery) {
+	  drupal_add_js($path . '/js/jquery.min.js', array( 'type' => 'file', 'scope' => 'header', 'weight' => 888 ));
+		drupal_add_js($path . '/js/slicknav/jquery-no-conflict.slicknav.js', array( 'type' => 'file', 'scope' => 'header', 'weight' => 888 ));
+		drupal_add_css($path . '/js/slicknav/slicknav.css');
+	}
+	elseif ($load_slicknav and !$load_jQuery) {
+		drupal_add_js($path . '/js/slicknav/jquery.slicknav.js', array( 'type' => 'file', 'scope' => 'header', 'weight' => 888 ));
+		drupal_add_css($path . '/js/slicknav/slicknav.css');
+	}
+
+	drupal_add_js($path . '/js/adminimal_admin_menu.js', array( 'type' => 'file', 'scope' => 'header', 'weight' => 888 ));
 
 
   if (!isset($page['page_bottom']['admin_menu'])) {
   if (!isset($page['page_bottom']['admin_menu'])) {
     return;
     return;
@@ -63,6 +99,7 @@ function adminimal_admin_menu_page_build(&$page) {
     'data' => array('admin_menu' => array('toolbar' => $settings)),
     'data' => array('admin_menu' => array('toolbar' => $settings)),
     'type' => 'setting',
     'type' => 'setting',
   );
   );
+
 }
 }
 
 
 /**
 /**
@@ -114,6 +151,31 @@ function adminimal_admin_menu_admin_menu_output_alter(&$content) {
       unset($content['menu'][$key]);
       unset($content['menu'][$key]);
     }
     }
   }
   }
+
+	// Create the responsive menu.
+	if (variable_get('adminimal_admin_menu_slicknav', 'TRUE')) {
+	  // Prepare the responsive menu and Join the Icon menu with the administration menu.
+	  $responsivemenu = array_merge($content['icon'], $content['menu']);
+	
+	  // Give it ID to target it later with js and css.
+	  $responsivemenu['#wrapper_attributes']['id'] = 'admin-menu-menu-responsive';
+	
+	  // Move the icon menu to the top.
+	  $responsivemenu['icon']['#weight'] = '-100';
+	
+	  // Change the link title to Administration.
+	  $responsivemenu['admin/index']['#title'] = t('Administration');
+	
+	  // Bind the responsive menu the the content varable so it can be rendered.
+	  $content['responsive-menu'] = $responsivemenu;
+	
+	  // Create the responsive shortucts.
+	  $content['responsive']['shortcuts'] = array(
+	    '#prefix' => '<div id="admin-menu-shortcuts-responsive">',
+	    '#suffix' => '</div>',
+	    '#pre_render' => array('shortcut_toolbar_pre_render'),
+	  );
+	}
 }
 }
 
 
 /**
 /**
@@ -126,8 +188,30 @@ function adminimal_admin_menu_preprocess_html(&$vars) {
   // Add the "adminimal" class to the body for better css selection.
   // Add the "adminimal" class to the body for better css selection.
   $vars['classes_array'][] = 'adminimal-menu';
   $vars['classes_array'][] = 'adminimal-menu';
 
 
+  // Add frontend and backend classes to the body.
+  if (path_is_admin(current_path())) {
+    $vars['classes_array'][] = 'adminimal-backend';
+  }
+  else {
+    $vars['classes_array'][] = 'adminimal-frontend';
+  }
+
   // Add the shortcut render mode class.
   // Add the shortcut render mode class.
-  $vars['classes_array'][] = 'menu-render-'.variable_get('adminimal_admin_menu_render', 'collapsed');
+  $vars['classes_array'][] = 'menu-render-' . variable_get('adminimal_admin_menu_render', 'collapsed');
+
+  // Fix the viewport, correcting the mobile device zoom.
+  /** @todo - Mybre remove this and let the theme manage the view pont.
+   *  But i was suprised that only few "responsive" drupal themes were fixing
+   *  the viewport.
+   */
+  $viewport = array(
+   '#tag' => 'meta',
+   '#attributes' => array(
+     'name' => 'viewport',
+     'content' => 'width=device-width, initial-scale=1, maximum-scale=1',
+   ),
+  );
+  drupal_add_html_head($viewport, 'viewport');
 
 
 }
 }
 
 

+ 35 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_menu_settings.inc

@@ -33,6 +33,41 @@ function adminimal_admin_menu_settings($form, &$form_state) {
     '#required' => TRUE,
     '#required' => TRUE,
   );
   );
 
 
+  // Create the shortcut category.
+  $form['advanced_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced Settings'),
+    '#description' => '<div class="messages warning">WARNING: Do not change any of the advanced setting unless you know what you are doing!</div>',
+  );
+
+	$form['advanced_settings']['adminimal_admin_menu_slicknav'] = array(
+	  '#type' => 'checkbox',
+	  '#default_value' => variable_get('adminimal_admin_menu_slicknav', 'TRUE'),
+	  '#title' => t('Enable Responsive Menu.'),
+	  '#description' => t('<strong>Default value => Checked</strong>. 
+	  Enable this option if you want to have responsive menu and mobile device support. 
+	  While disabling this option could save you few kilobytes (around 3KB), i will completely disable the responsive menu functionality.'),
+	);
+
+	$form['advanced_settings']['adminimal_admin_menu_jquery'] = array(
+	  '#type' => 'checkbox',
+	  '#default_value' => variable_get('adminimal_admin_menu_jquery', 'TRUE'),
+	  '#title' => t('Load the requred jQuery 1.7 library automagically.'),
+	  '#description' => t('<strong>Default value => Checked</strong>. This will load the newer jQuery version 1.7 using 
+	  the no-conflict method so it wont interfere with any existing jQuery or other java-script libraries. 
+	  The only reason to uncheck this if you are already using a newer version of jQuery site-wide and its globally accessible by the "$" variable. 
+	  Unchekig this option could save you 33KB, but it may also break your javasctipt if not used correctly.'),
+    '#states' => array(
+      // Hide the settings when the cancel notify checkbox is disabled.
+      'visible' => array(
+       ':input[name="adminimal_admin_menu_slicknav"]' => array('checked' => TRUE),
+      ),
+      'unchecked' => array(
+       variable_get('adminimal_admin_menu_jquery', 'TRUE') => FALSE,
+      ),
+     ),
+	);
+
   // Create the submit button.
   // Create the submit button.
   $form['submit'] = array(
   $form['submit'] = array(
     '#type' => 'submit',
     '#type' => 'submit',

+ 2 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/credits.txt

@@ -0,0 +1,2 @@
+Designed and developed by: ANDiTKO -> http://anditko.com
+Some of the Icons are from the Icons8 set -> http://icons8.com/ and -> http://VisualPharm.com

File diff suppressed because it is too large
+ 6 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/circledown.svg


+ 14 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/home.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px"
+	 height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="bg" display="none">
+	<rect display="inline" fill="#3F3F3F" width="16" height="16"/>
+</g>
+<g id="Layer_3">
+	<polygon id="Houde" fill="#FFFFFF" points="8,1.5 1.002,7.498 2.954,7.499 2.964,13.496 5.984,13.496 5.984,9.783 9.984,9.783 
+		9.984,13.496 12.999,13.496 12.999,7.498 14.967,7.498 	"/>
+	<rect x="10.313" y="1.871" fill="#FFFFFF" width="1.884" height="5"/>
+</g>
+</svg>

+ 9 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/house.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="20px" height="20px" viewBox="0 -2 20 20" enable-background="new 0 -2 20 20" xml:space="preserve">
+<polygon id="Houde" fill="#FFFFFF" points="10.022,-0.875 -0.076,8.494 2.739,8.495 2.755,17.861 7.113,17.861 7.113,12.063 
+	12.885,12.063 12.885,17.861 17.235,17.861 17.235,8.494 20.076,8.494 "/>
+<rect x="13.359" y="-0.339" fill="#FFFFFF" width="2.719" height="7.215"/>
+</svg>

+ 11 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/menu-arrows.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generated by IcoMoon.io -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="47" height="16" viewBox="0 0 47 16">
+<g transform="translate(0 0)">
+	<path d="M5.143 8q0 0.232-0.17 0.402l-4 4q-0.17 0.17-0.402 0.17t-0.402-0.17-0.17-0.402v-8q0-0.232 0.17-0.402t0.402-0.17 0.402 0.17l4 4q0.17 0.17 0.17 0.402z" fill="#ffffff" />
+</g>
+<g transform="translate(38 0)">
+	<path d="M9.143 9.714q0 0.232-0.17 0.402l-4 4q-0.17 0.17-0.402 0.17t-0.402-0.17l-4-4q-0.17-0.17-0.17-0.402t0.17-0.402 0.402-0.17h8q0.232 0 0.402 0.17t0.17 0.402z" fill="#ffffff" />
+</g>
+</svg>

+ 50 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/menu.svg

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg [
+	<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
+	<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+]>
+<svg version="1.2" baseProfile="tiny" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
+	 x="0px" y="0px" width="20px" height="16px" viewBox="0 0 20 16" xml:space="preserve">
+<g id="Layer_2" display="none">
+	<rect x="-3" y="-2" display="inline" fill="#1A1A1A" width="33" height="23"/>
+</g>
+<g id="Layer_1">
+	<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" x1="3" y1="3.5" x2="17" y2="3.5"/>
+	<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" x1="3" y1="8.5" x2="17" y2="8.5"/>
+	<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" x1="3" y1="13.5" x2="17" y2="13.5"/>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+	<g>
+	</g>
+</g>
+</svg>

+ 27 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/images/tasks.svg

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="20px" height="16px" viewBox="0 0 20 16" enable-background="new 0 0 20 16" xml:space="preserve">
+<g id="Layer_2" display="none">
+	<rect x="-3" y="-2" display="inline" fill="#1A1A1A" width="33" height="23"/>
+</g>
+<path fill="#FFFFFF" d="M15.795,6.872c-0.25-0.037-0.516-0.261-0.593-0.5l-0.37-0.901c-0.116-0.222-0.089-0.569,0.061-0.77
+	L15.495,3.9c0.15-0.201,0.138-0.52-0.028-0.707l-0.66-0.661c-0.188-0.166-0.506-0.178-0.708-0.027l-0.802,0.601
+	c-0.2,0.151-0.546,0.178-0.769,0.061l-0.901-0.37c-0.24-0.077-0.465-0.342-0.498-0.591l-0.143-0.991
+	c-0.035-0.249-0.268-0.474-0.518-0.5c0,0-0.154-0.016-0.468-0.016c-0.313,0-0.469,0.016-0.469,0.016
+	c-0.25,0.027-0.482,0.251-0.518,0.5L8.872,2.205c-0.036,0.25-0.26,0.515-0.499,0.591l-0.901,0.37
+	C7.25,3.283,6.904,3.256,6.703,3.105L5.901,2.503C5.7,2.353,5.381,2.365,5.193,2.531l-0.66,0.661
+	C4.368,3.38,4.354,3.698,4.506,3.899l0.601,0.802C5.258,4.902,5.285,5.248,5.169,5.47L4.797,6.372
+	c-0.076,0.239-0.342,0.464-0.591,0.5l-0.991,0.14c-0.249,0.036-0.473,0.269-0.5,0.519c0,0-0.016,0.154-0.016,0.469
+	s0.016,0.469,0.016,0.469c0.027,0.25,0.251,0.483,0.5,0.52l0.991,0.141c0.249,0.035,0.515,0.26,0.591,0.498l0.372,0.902
+	c0.116,0.221,0.089,0.568-0.062,0.768L4.506,12.1c-0.151,0.201-0.188,0.475-0.081,0.605c0.106,0.131,0.412,0.457,0.413,0.459
+	c0,0,0.104,0.096,0.228,0.209c0.125,0.113,0.634,0.273,0.835,0.123l0.802-0.602c0.201-0.15,0.546-0.18,0.77-0.061l0.9,0.369
+	c0.239,0.076,0.464,0.342,0.499,0.592l0.142,0.992c0.035,0.248,0.268,0.473,0.519,0.5c0,0,0.155,0.016,0.468,0.016
+	s0.469-0.016,0.469-0.016c0.248-0.027,0.482-0.252,0.518-0.5l0.143-0.992c0.033-0.248,0.258-0.516,0.498-0.592l0.9-0.371
+	c0.223-0.117,0.568-0.09,0.77,0.063l0.802,0.602c0.202,0.15,0.521,0.139,0.708-0.027l0.659-0.66
+	c0.166-0.189,0.178-0.508,0.028-0.709l-0.603-0.803c-0.149-0.199-0.177-0.547-0.061-0.768l0.371-0.902
+	c0.077-0.238,0.342-0.463,0.592-0.498l0.99-0.141c0.249-0.037,0.474-0.27,0.5-0.52c0,0,0.017-0.154,0.017-0.469
+	s-0.017-0.469-0.017-0.469c-0.026-0.249-0.251-0.483-0.5-0.519L15.795,6.872z M10,9.826C8.992,9.826,8.175,9.008,8.175,8
+	c0-1.009,0.817-1.826,1.826-1.826c1.008,0,1.826,0.818,1.826,1.826C11.826,9.008,11.008,9.826,10,9.826z"/>
+</svg>

+ 12 - 16
sites/all/modules/contrib/admin/adminimal_admin_menu/adminimal_admin_menu.js → sites/all/modules/contrib/admin/adminimal_admin_menu/js/adminimal_admin_menu.js

@@ -19,43 +19,39 @@ Drupal.admin.behaviors.toolbarActiveTrail = function (context, settings, $adminM
   }
   }
 };
 };
 
 
-/**
- * @} End of "ingroup admin_behaviors".
- */
-
 Drupal.admin.behaviors.shorcutcollapsed = function (context, settings, $adminMenu) {
 Drupal.admin.behaviors.shorcutcollapsed = function (context, settings, $adminMenu) {
 
 
   // Create the dropdown base 
   // Create the dropdown base 
-  $("<li class=\"label\"><a>"+Drupal.t('Shortcuts')+"</a></li>").prependTo("body.menu-render-collapsed div.toolbar-shortcuts ul"); 
+  $("<li class=\"label\"><a>"+Drupal.t('Shortcuts')+"</a></li>").prependTo("body.menu-render-collapsed #toolbar div.toolbar-shortcuts ul"); 
 
 
-}
+};
 
 
 Drupal.admin.behaviors.shorcutselect = function (context, settings, $adminMenu) {
 Drupal.admin.behaviors.shorcutselect = function (context, settings, $adminMenu) {
 
 
   // Create the dropdown base
   // Create the dropdown base
-  $("<select id='shortcut-menu'/>").appendTo("body.menu-render-dropdown div.toolbar-shortcuts");
-    
+  $("<select id='shortcut-menu'/>").appendTo("body.menu-render-dropdown #toolbar div.toolbar-shortcuts");
+
   // Create default option "Select"
   // Create default option "Select"
   $("<option />", {
   $("<option />", {
     "selected"  :  "selected",
     "selected"  :  "selected",
     "value"     :  "",
     "value"     :  "",
     "text"      :  Drupal.t('Shortcuts')
     "text"      :  Drupal.t('Shortcuts')
-  }).appendTo("body.menu-render-dropdown div.toolbar-shortcuts select");
-    
+  }).appendTo("body.menu-render-dropdown #toolbar div.toolbar-shortcuts select");
+
   // Populate dropdown with menu items
   // Populate dropdown with menu items
-  $("body.menu-render-dropdown div.toolbar-shortcuts a").each(function() {
+  $("body.menu-render-dropdown #toolbar div.toolbar-shortcuts a").each(function() {
     var el = $(this);
     var el = $(this);
     $("<option />", {
     $("<option />", {
       "value"   :  el.attr("href"),
       "value"   :  el.attr("href"),
       "text"    :  el.text()
       "text"    :  el.text()
-    }).appendTo("body.menu-render-dropdown div.toolbar-shortcuts select");
+    }).appendTo("body.menu-render-dropdown #toolbar div.toolbar-shortcuts select");
     });
     });
-    
-  $("body.menu-render-dropdown div.toolbar-shortcuts select").change(function() {
+
+  $("body.menu-render-dropdown #toolbar div.toolbar-shortcuts select").change(function() {
     window.location = $(this).find("option:selected").val();
     window.location = $(this).find("option:selected").val();
   });
   });
-  
-  $('body.menu-render-dropdown div.toolbar-shortcuts ul').remove();
+
+  $('body.menu-render-dropdown #toolbar div.toolbar-shortcuts ul').remove();
 
 
 };
 };
 
 

File diff suppressed because it is too large
+ 1 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/jquery.min.js


+ 19 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/MIT-LICENSE.txt

@@ -0,0 +1,19 @@
+Copyright (c) 2014 Josh Cope
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 90 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/README.md

@@ -0,0 +1,90 @@
+# SlickNav
+## Responsive Mobile Menu jQuery Plugin
+
+###[SlickNav.com](http://slicknav.com)
+
+### Features
+* Multi-level menu support
+* Flexible, simple markup
+* Cross-browser compatibility
+* Keyboard Accessible
+* Degrades gracefully without JavaScript
+* Creates ARIA compliant menu
+
+* * *
+### Usage
+
+####Include the CSS & JS
+slicknav.css can be modified to fit website design
+
+    <link rel="stylesheet" href="SlickNav/slicknav.css" />
+	<script src="SlickNav/jquery.slicknav.min.js"></script>
+
+####Menu Markup
+
+    <ul id="menu">
+        <li><a href="#">item 1</a></li>
+        <li><a href="#">item 2</a></li>
+    	<li><a href="#">item 3</a></li>
+    	<li><a href="#">item 4</a></li>
+    </ul>
+####Initialize
+
+	<script>
+		$(function(){
+			$('#menu').slicknav();
+		});
+	</script>	
+
+### Options
+	'label' : 'MENU', // Label for menu button. Use an empty string for no label.
+	'duplicate': true, // If true, the mobile menu is a copy of the original.
+	'duration': true, // The duration of the sliding animation.
+	'easingOpen': 'swing', // Easing used for open animations.
+	'easingClose': 'swing' // Easing used for close animations.
+	'closedSymbol': '&#9658;', // Character after collapsed parents.
+	'openedSymbol': '&#9660;', // Character after expanded parents.
+	'prependTo': 'body', // Element, jQuery object, or jQuery selector string to prepend the mobile menu to.
+	'parentTag': 'a', // Element type for parent menu items.
+	'closeOnClick': false, // Close menu when a link is clicked.
+	'allowParentLinks': false // Allow clickable links as parent elements.
+	
+### Callbacks
+	'init': function(){}, // Called after SlickNav creation
+	'open': function(trigger){}, // Called after menu or sub-menu opened. 
+	'close': function(trigger){} // Called after menu or sub-menu closed.
+
+### Methods
+	$('.menu').slicknav('toggle'); // Method to toggle the menu
+	$('.menu').slicknav('open'); // Method to open the menu
+	$('.menu').slicknav('close'); // Method to close the menu
+    
+Without any additional configuration, both the original and mobile menus will be displayed. It is recommended to use media queries to hide the original menu and display the mobile menu when appropriate. Modernizr or similar can be used for graceful degradation.
+
+For example:
+
+    .slicknav_menu {
+        display:none;
+    }
+    
+    @media screen and (max-width: 40em) {
+    	/* #menu is the original menu */
+    	.js #menu {
+    		display:none;
+    	}
+    	
+    	.js .slicknav_menu {
+    		display:block;
+    	}
+    }
+
+More examples at [SlickNav.com](http://slicknav.com)
+
+### Browser Support
+* Chrome
+* Firefox
+* Safari
+* Opera
+* IE7+
+* Android Browser
+* iOS Safari

+ 432 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/jquery-no-conflict.slicknav.js

@@ -0,0 +1,432 @@
+/*!
+	SlickNav Responsive Mobile Menu
+	(c) 2014 Josh Cope
+	licensed under MIT
+*/
+;(function ($, document, window) {
+	var
+	// default settings object.
+	defaults = {
+		label: 'MENU',
+		duplicate: true,
+		duration: 200,
+		easingOpen: 'swing',
+		easingClose: 'swing',
+		closedSymbol: '&#9658;',
+		openedSymbol: '&#9660;',
+		prependTo: 'body',
+		parentTag: 'a',
+		closeOnClick: false,
+		allowParentLinks: false,
+		init: function(){},
+		open: function(){},
+		close: function(){}
+	},
+	mobileMenu = 'slicknav',
+	prefix = 'slicknav';
+	
+	function Plugin( element, options ) {
+		this.element = element;
+
+        // jQuery has an extend method which merges the contents of two or
+        // more objects, storing the result in the first object. The first object
+        // is generally empty as we don't want to alter the default options for
+        // future instances of the plugin
+        this.settings = $.extend( {}, defaults, options) ;
+        
+        this._defaults = defaults;
+        this._name = mobileMenu;
+        
+        this.init();
+	}
+	
+	Plugin.prototype.init = function () {
+        var $this = this;
+		var menu = $(this.element);
+		var settings = this.settings;
+		
+		// clone menu if needed
+		if (settings.duplicate) {
+			$this.mobileNav = menu.clone();
+			//remove ids from clone to prevent css issues
+			$this.mobileNav.removeAttr('id');
+			$this.mobileNav.find('*').each(function(i,e){
+				$(e).removeAttr('id');
+			});
+		}
+		else
+			$this.mobileNav = menu;
+		
+		// styling class for the button
+		var iconClass = prefix+'_icon';
+		
+		if (settings.label == '') {
+			iconClass += ' '+prefix+'_no-text';
+		}
+		
+		if (settings.parentTag == 'a') {
+			settings.parentTag = 'a href="#"';
+		}
+		
+		// create menu bar
+		$this.mobileNav.attr('class', prefix+'_nav');
+		var menuBar = $('<div class="'+prefix+'_menu"></div>');
+		$this.btn = $('<'+settings.parentTag+' aria-haspopup="true" tabindex="0" class="'+prefix+'_btn '+prefix+'_collapsed"><span class="'+prefix+'_menutxt">'+settings.label+'</span><span class="'+iconClass+'"><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span></span></a>');
+		$(menuBar).append($this.btn);		
+		$(settings.prependTo).prepend(menuBar);
+		menuBar.append($this.mobileNav);
+		
+		// iterate over structure adding additional structure
+		var items = $this.mobileNav.find('li');
+		$(items).each(function () {
+			var item = $(this);
+			data = {};
+			data.children = item.children('ul').attr('role','menu');
+			item.data("menu", data);
+			
+			// if a list item has a nested menu
+			if (data.children.length > 0) {
+			
+				// select all text before the child menu
+				var a = item.contents();
+				var nodes = [];
+				$(a).each(function(){
+					if(!$(this).is("ul")) {
+						nodes.push(this);
+					}
+					else {
+						return false;
+					}
+				});
+				
+				// wrap item text with tag and add classes
+				var wrap = $(nodes).wrapAll('<'+settings.parentTag+' role="menuitem" aria-haspopup="true" tabindex="-1" class="'+prefix+'_item"/>').parent();
+				
+				item.addClass(prefix+'_collapsed');
+				item.addClass(prefix+'_parent');
+				
+				// create parent arrow
+				$(nodes).last().after('<span class="'+prefix+'_arrow">'+settings.closedSymbol+'</span>');
+				
+			
+			} else if ( item.children().length == 0) {
+				 item.addClass(prefix+'_txtnode');
+			}
+			
+			// accessibility for links
+			item.children('a').attr('role', 'menuitem').click(function(){
+				//Emulate menu close if set
+				if (settings.closeOnClick)
+					$($this.btn).click();
+			});
+		});
+		
+		// structure is in place, now hide appropriate items
+		$(items).each(function () {
+			var data = $(this).data("menu");
+			$this._visibilityToggle(data.children, false, null, true);
+		});
+		
+		// finally toggle entire menu
+		$this._visibilityToggle($this.mobileNav, false, 'init', true);
+		
+		// accessibility for menu button
+		$this.mobileNav.attr('role','menu');
+		
+		// outline prevention when using mouse
+		$(document).mousedown(function(){
+			$this._outlines(false);
+		});
+		
+		$(document).keyup(function(){
+			$this._outlines(true);
+		});
+		
+		// menu button click
+		$($this.btn).click(function (e) {
+			e.preventDefault();
+			$this._menuToggle();			
+		});
+		
+		// click on menu parent
+		$this.mobileNav.on('click', '.'+prefix+'_item', function(e){
+			e.preventDefault();
+			$this._itemClick($(this));
+		});
+		
+		// check for enter key on menu button and menu parents
+		$($this.btn).keydown(function (e) {
+			var ev = e || event;
+			if(ev.keyCode == 13) {
+				e.preventDefault();
+				$this._menuToggle();
+			}
+		});
+		
+		$this.mobileNav.on('keydown', '.'+prefix+'_item', function(e) {
+			var ev = e || event;
+			if(ev.keyCode == 13) {
+				e.preventDefault();
+				$this._itemClick($(e.target));
+			}
+		});
+		
+		// allow links clickable within parent tags if set
+		if (settings.allowParentLinks) {
+			$('.'+prefix+'_item a').click(function(e){
+					e.stopImmediatePropagation();
+			});
+		}
+    };
+	
+	//toggle menu
+	Plugin.prototype._menuToggle = function(el){
+		var $this = this;
+		var btn = $this.btn;
+		var mobileNav = $this.mobileNav;
+		
+		if (btn.hasClass(prefix+'_collapsed')) {
+			btn.removeClass(prefix+'_collapsed');
+			btn.addClass(prefix+'_open');
+		} else {
+			btn.removeClass(prefix+'_open');
+			btn.addClass(prefix+'_collapsed');
+		}
+		btn.addClass(prefix+'_animating');
+		$this._visibilityToggle(mobileNav, true, btn);
+	}
+	
+	// toggle clicked items
+	Plugin.prototype._itemClick = function(el) {
+		var $this = this;
+		var settings = $this.settings;
+		var data = el.data("menu");
+		if (!data) {
+			data = {};
+			data.arrow = el.children('.'+prefix+'_arrow');
+			data.ul = el.next('ul');
+			data.parent = el.parent();
+			el.data("menu", data);
+		}
+		if (data.parent.hasClass(prefix+'_collapsed')) {
+			data.arrow.html(settings.openedSymbol);
+			data.parent.removeClass(prefix+'_collapsed');
+			data.parent.addClass(prefix+'_open');
+			data.parent.addClass(prefix+'_animating');
+			$this._visibilityToggle(data.ul, true, el);
+		} else {
+			data.arrow.html(settings.closedSymbol);
+			data.parent.addClass(prefix+'_collapsed');
+			data.parent.removeClass(prefix+'_open');
+			data.parent.addClass(prefix+'_animating');
+			$this._visibilityToggle(data.ul, true, el);
+		}
+	}
+
+	// toggle actual visibility and accessibility tags
+	Plugin.prototype._visibilityToggle = function(el, animate, trigger, init) {
+		var $this = this;
+		var settings = $this.settings;
+		var items = $this._getActionItems(el);
+		var duration = 0;
+		if (animate)
+			duration = settings.duration;
+		
+		if (el.hasClass(prefix+'_hidden')) {
+			el.removeClass(prefix+'_hidden');
+			el.slideDown(duration, settings.easingOpen, function(){
+				
+				$(trigger).removeClass(prefix+'_animating');
+				$(trigger).parent().removeClass(prefix+'_animating');
+				
+				//Fire open callback
+				if (!init) {
+					settings.open(trigger);
+				}
+			});
+			el.attr('aria-hidden','false');
+			items.attr('tabindex', '0');
+			$this._setVisAttr(el, false);
+		} else {
+			el.addClass(prefix+'_hidden');
+			el.slideUp(duration, this.settings.easingClose, function() {
+				el.attr('aria-hidden','true');
+				items.attr('tabindex', '-1');
+				$this._setVisAttr(el, true);
+				el.hide(); //jQuery 1.7 bug fix
+				
+				$(trigger).removeClass(prefix+'_animating');
+				$(trigger).parent().removeClass(prefix+'_animating');
+				
+				//Fire init or close callback
+				if (!init)
+					settings.close(trigger);
+				else if (trigger == 'init')
+					settings.init();
+			});
+		}
+	}
+
+	// set attributes of element and children based on visibility
+	Plugin.prototype._setVisAttr = function(el, hidden) {
+		var $this = this;
+		
+		// select all parents that aren't hidden
+		var nonHidden = el.children('li').children('ul').not('.'+prefix+'_hidden');
+		
+		// iterate over all items setting appropriate tags
+		if (!hidden) {
+			nonHidden.each(function(){
+				var ul = $(this);
+				ul.attr('aria-hidden','false');
+				var items = $this._getActionItems(ul);
+				items.attr('tabindex', '0');
+				$this._setVisAttr(ul, hidden);
+			});
+		} else {
+			nonHidden.each(function(){
+				var ul = $(this);
+				ul.attr('aria-hidden','true');
+				var items = $this._getActionItems(ul);
+				items.attr('tabindex', '-1');
+				$this._setVisAttr(ul, hidden);
+			});
+		}
+	}
+
+	// get all 1st level items that are clickable
+	Plugin.prototype._getActionItems = function(el) {
+		var data = el.data("menu");
+		if (!data) {
+			data = {};
+			var items = el.children('li');
+			var anchors = items.children('a');
+			data.links = anchors.add(items.children('.'+prefix+'_item'));
+			el.data("menu", data);
+		}
+		return data.links;
+	}
+
+	Plugin.prototype._outlines = function(state) {
+		if (!state) {
+			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','none');
+		} else {
+			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','');
+		}
+	}
+	
+	Plugin.prototype.toggle = function(){
+		$this._menuToggle();
+	}
+	
+	Plugin.prototype.open = function(){
+		$this = this;
+		if ($this.btn.hasClass(prefix+'_collapsed')) {
+			$this._menuToggle();
+		}
+	}
+	
+	Plugin.prototype.close = function(){
+		$this = this;
+		if ($this.btn.hasClass(prefix+'_open')) {
+			$this._menuToggle();
+		}
+	}
+	
+	$.fn[mobileMenu] = function ( options ) {
+		var args = arguments;
+
+		// Is the first parameter an object (options), or was omitted, instantiate a new instance
+		if (options === undefined || typeof options === 'object') {
+			return this.each(function () {
+
+				// Only allow the plugin to be instantiated once due to methods
+				if (!$.data(this, 'plugin_' + mobileMenu)) {
+
+					// if it has no instance, create a new one, pass options to our plugin constructor,
+					// and store the plugin instance in the elements jQuery data object.
+					$.data(this, 'plugin_' + mobileMenu, new Plugin( this, options ));
+				}
+			});
+
+		// If is a string and doesn't start with an underscore or 'init' function, treat this as a call to a public method.
+		} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
+
+			// Cache the method call to make it possible to return a value
+			var returns;
+
+			this.each(function () {
+				var instance = $.data(this, 'plugin_' + mobileMenu);
+
+				// Tests that there's already a plugin-instance and checks that the requested public method exists
+				if (instance instanceof Plugin && typeof instance[options] === 'function') {
+
+					// Call the method of our plugin instance, and pass it the supplied arguments.
+					returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
+				}
+			});
+
+			// If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability.
+			return returns !== undefined ? returns : this;
+		}
+	};
+}($jQueryAdminimal, document, window));
+
+(function($) {
+
+// Create the responsive menu using SlickNav.
+Drupal.admin.behaviors.responsivemenu = function (context, settings, $adminMenu) {
+
+    $('#admin-menu-menu-responsive').slicknav({
+		label: 'Menu',
+		prependTo:'body',
+		closedSymbol: "<i class=\"closed\"></i>",
+		openedSymbol: "<i class=\"open\"></i>",
+		allowParentLinks: true
+	});
+
+};
+
+// Create the responsive shortcuts dropdown.
+Drupal.admin.behaviors.responsiveshortcuts = function (context, settings, $adminMenu) {
+
+  // Check if there are any shortucts to respondify.
+  if(jQuery("div.toolbar-shortcuts ul.menu li").length){
+
+	  // Create the dropdown base
+	  $("<select id='responsive-shortcuts-dropdown'/>").appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts");
+
+	  // Create default option "Select"
+	  $("<option />", {
+	    "selected"  :  "selected",
+	    "class"     :  "hide",
+	    "value"     :  "",
+	    "text"      :  Drupal.t('Shortcuts')
+	  }).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
+
+	  // Populate dropdown with menu items
+	  $("#admin-menu-shortcuts-responsive div.toolbar-shortcuts a").each(function() {
+	    var el = $(this);
+	    $("<option />", {
+	      "value"   :  el.attr("href"),
+	      "text"    :  el.text()
+	    }).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
+	  });
+
+      // Redirect the user when selecting an option.
+	  $("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select").change(function() {
+	    window.location = $(this).find("option:selected").val();
+	  });
+
+	  // Clean the mess.
+	  $('#admin-menu-shortcuts-responsive div.toolbar-shortcuts ul').remove();
+	  // Move the select box into the responsive menu.
+	  $("#admin-menu-shortcuts-responsive").prependTo(".slicknav_menu");
+
+	  }
+
+  // Remove the edit shortcuts link from the DOM to avoid duble rendering.
+  $('#admin-menu-shortcuts-responsive #edit-shortcuts').remove();
+
+};
+})($jQueryAdminimal);

+ 432 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/jquery.slicknav.js

@@ -0,0 +1,432 @@
+/*!
+	SlickNav Responsive Mobile Menu
+	(c) 2014 Josh Cope
+	licensed under MIT
+*/
+;(function ($, document, window) {
+	var
+	// default settings object.
+	defaults = {
+		label: 'MENU',
+		duplicate: true,
+		duration: 200,
+		easingOpen: 'swing',
+		easingClose: 'swing',
+		closedSymbol: '&#9658;',
+		openedSymbol: '&#9660;',
+		prependTo: 'body',
+		parentTag: 'a',
+		closeOnClick: false,
+		allowParentLinks: false,
+		init: function(){},
+		open: function(){},
+		close: function(){}
+	},
+	mobileMenu = 'slicknav',
+	prefix = 'slicknav';
+	
+	function Plugin( element, options ) {
+		this.element = element;
+
+        // jQuery has an extend method which merges the contents of two or
+        // more objects, storing the result in the first object. The first object
+        // is generally empty as we don't want to alter the default options for
+        // future instances of the plugin
+        this.settings = $.extend( {}, defaults, options) ;
+        
+        this._defaults = defaults;
+        this._name = mobileMenu;
+        
+        this.init();
+	}
+	
+	Plugin.prototype.init = function () {
+        var $this = this;
+		var menu = $(this.element);
+		var settings = this.settings;
+		
+		// clone menu if needed
+		if (settings.duplicate) {
+			$this.mobileNav = menu.clone();
+			//remove ids from clone to prevent css issues
+			$this.mobileNav.removeAttr('id');
+			$this.mobileNav.find('*').each(function(i,e){
+				$(e).removeAttr('id');
+			});
+		}
+		else
+			$this.mobileNav = menu;
+		
+		// styling class for the button
+		var iconClass = prefix+'_icon';
+		
+		if (settings.label == '') {
+			iconClass += ' '+prefix+'_no-text';
+		}
+		
+		if (settings.parentTag == 'a') {
+			settings.parentTag = 'a href="#"';
+		}
+		
+		// create menu bar
+		$this.mobileNav.attr('class', prefix+'_nav');
+		var menuBar = $('<div class="'+prefix+'_menu"></div>');
+		$this.btn = $('<'+settings.parentTag+' aria-haspopup="true" tabindex="0" class="'+prefix+'_btn '+prefix+'_collapsed"><span class="'+prefix+'_menutxt">'+settings.label+'</span><span class="'+iconClass+'"><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span></span></a>');
+		$(menuBar).append($this.btn);		
+		$(settings.prependTo).prepend(menuBar);
+		menuBar.append($this.mobileNav);
+		
+		// iterate over structure adding additional structure
+		var items = $this.mobileNav.find('li');
+		$(items).each(function () {
+			var item = $(this);
+			data = {};
+			data.children = item.children('ul').attr('role','menu');
+			item.data("menu", data);
+			
+			// if a list item has a nested menu
+			if (data.children.length > 0) {
+			
+				// select all text before the child menu
+				var a = item.contents();
+				var nodes = [];
+				$(a).each(function(){
+					if(!$(this).is("ul")) {
+						nodes.push(this);
+					}
+					else {
+						return false;
+					}
+				});
+				
+				// wrap item text with tag and add classes
+				var wrap = $(nodes).wrapAll('<'+settings.parentTag+' role="menuitem" aria-haspopup="true" tabindex="-1" class="'+prefix+'_item"/>').parent();
+				
+				item.addClass(prefix+'_collapsed');
+				item.addClass(prefix+'_parent');
+				
+				// create parent arrow
+				$(nodes).last().after('<span class="'+prefix+'_arrow">'+settings.closedSymbol+'</span>');
+				
+			
+			} else if ( item.children().length == 0) {
+				 item.addClass(prefix+'_txtnode');
+			}
+			
+			// accessibility for links
+			item.children('a').attr('role', 'menuitem').click(function(){
+				//Emulate menu close if set
+				if (settings.closeOnClick)
+					$($this.btn).click();
+			});
+		});
+		
+		// structure is in place, now hide appropriate items
+		$(items).each(function () {
+			var data = $(this).data("menu");
+			$this._visibilityToggle(data.children, false, null, true);
+		});
+		
+		// finally toggle entire menu
+		$this._visibilityToggle($this.mobileNav, false, 'init', true);
+		
+		// accessibility for menu button
+		$this.mobileNav.attr('role','menu');
+		
+		// outline prevention when using mouse
+		$(document).mousedown(function(){
+			$this._outlines(false);
+		});
+		
+		$(document).keyup(function(){
+			$this._outlines(true);
+		});
+		
+		// menu button click
+		$($this.btn).click(function (e) {
+			e.preventDefault();
+			$this._menuToggle();			
+		});
+		
+		// click on menu parent
+		$this.mobileNav.on('click', '.'+prefix+'_item', function(e){
+			e.preventDefault();
+			$this._itemClick($(this));
+		});
+		
+		// check for enter key on menu button and menu parents
+		$($this.btn).keydown(function (e) {
+			var ev = e || event;
+			if(ev.keyCode == 13) {
+				e.preventDefault();
+				$this._menuToggle();
+			}
+		});
+		
+		$this.mobileNav.on('keydown', '.'+prefix+'_item', function(e) {
+			var ev = e || event;
+			if(ev.keyCode == 13) {
+				e.preventDefault();
+				$this._itemClick($(e.target));
+			}
+		});
+		
+		// allow links clickable within parent tags if set
+		if (settings.allowParentLinks) {
+			$('.'+prefix+'_item a').click(function(e){
+					e.stopImmediatePropagation();
+			});
+		}
+    };
+	
+	//toggle menu
+	Plugin.prototype._menuToggle = function(el){
+		var $this = this;
+		var btn = $this.btn;
+		var mobileNav = $this.mobileNav;
+		
+		if (btn.hasClass(prefix+'_collapsed')) {
+			btn.removeClass(prefix+'_collapsed');
+			btn.addClass(prefix+'_open');
+		} else {
+			btn.removeClass(prefix+'_open');
+			btn.addClass(prefix+'_collapsed');
+		}
+		btn.addClass(prefix+'_animating');
+		$this._visibilityToggle(mobileNav, true, btn);
+	}
+	
+	// toggle clicked items
+	Plugin.prototype._itemClick = function(el) {
+		var $this = this;
+		var settings = $this.settings;
+		var data = el.data("menu");
+		if (!data) {
+			data = {};
+			data.arrow = el.children('.'+prefix+'_arrow');
+			data.ul = el.next('ul');
+			data.parent = el.parent();
+			el.data("menu", data);
+		}
+		if (data.parent.hasClass(prefix+'_collapsed')) {
+			data.arrow.html(settings.openedSymbol);
+			data.parent.removeClass(prefix+'_collapsed');
+			data.parent.addClass(prefix+'_open');
+			data.parent.addClass(prefix+'_animating');
+			$this._visibilityToggle(data.ul, true, el);
+		} else {
+			data.arrow.html(settings.closedSymbol);
+			data.parent.addClass(prefix+'_collapsed');
+			data.parent.removeClass(prefix+'_open');
+			data.parent.addClass(prefix+'_animating');
+			$this._visibilityToggle(data.ul, true, el);
+		}
+	}
+
+	// toggle actual visibility and accessibility tags
+	Plugin.prototype._visibilityToggle = function(el, animate, trigger, init) {
+		var $this = this;
+		var settings = $this.settings;
+		var items = $this._getActionItems(el);
+		var duration = 0;
+		if (animate)
+			duration = settings.duration;
+		
+		if (el.hasClass(prefix+'_hidden')) {
+			el.removeClass(prefix+'_hidden');
+			el.slideDown(duration, settings.easingOpen, function(){
+				
+				$(trigger).removeClass(prefix+'_animating');
+				$(trigger).parent().removeClass(prefix+'_animating');
+				
+				//Fire open callback
+				if (!init) {
+					settings.open(trigger);
+				}
+			});
+			el.attr('aria-hidden','false');
+			items.attr('tabindex', '0');
+			$this._setVisAttr(el, false);
+		} else {
+			el.addClass(prefix+'_hidden');
+			el.slideUp(duration, this.settings.easingClose, function() {
+				el.attr('aria-hidden','true');
+				items.attr('tabindex', '-1');
+				$this._setVisAttr(el, true);
+				el.hide(); //jQuery 1.7 bug fix
+				
+				$(trigger).removeClass(prefix+'_animating');
+				$(trigger).parent().removeClass(prefix+'_animating');
+				
+				//Fire init or close callback
+				if (!init)
+					settings.close(trigger);
+				else if (trigger == 'init')
+					settings.init();
+			});
+		}
+	}
+
+	// set attributes of element and children based on visibility
+	Plugin.prototype._setVisAttr = function(el, hidden) {
+		var $this = this;
+		
+		// select all parents that aren't hidden
+		var nonHidden = el.children('li').children('ul').not('.'+prefix+'_hidden');
+		
+		// iterate over all items setting appropriate tags
+		if (!hidden) {
+			nonHidden.each(function(){
+				var ul = $(this);
+				ul.attr('aria-hidden','false');
+				var items = $this._getActionItems(ul);
+				items.attr('tabindex', '0');
+				$this._setVisAttr(ul, hidden);
+			});
+		} else {
+			nonHidden.each(function(){
+				var ul = $(this);
+				ul.attr('aria-hidden','true');
+				var items = $this._getActionItems(ul);
+				items.attr('tabindex', '-1');
+				$this._setVisAttr(ul, hidden);
+			});
+		}
+	}
+
+	// get all 1st level items that are clickable
+	Plugin.prototype._getActionItems = function(el) {
+		var data = el.data("menu");
+		if (!data) {
+			data = {};
+			var items = el.children('li');
+			var anchors = items.children('a');
+			data.links = anchors.add(items.children('.'+prefix+'_item'));
+			el.data("menu", data);
+		}
+		return data.links;
+	}
+
+	Plugin.prototype._outlines = function(state) {
+		if (!state) {
+			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','none');
+		} else {
+			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','');
+		}
+	}
+	
+	Plugin.prototype.toggle = function(){
+		$this._menuToggle();
+	}
+	
+	Plugin.prototype.open = function(){
+		$this = this;
+		if ($this.btn.hasClass(prefix+'_collapsed')) {
+			$this._menuToggle();
+		}
+	}
+	
+	Plugin.prototype.close = function(){
+		$this = this;
+		if ($this.btn.hasClass(prefix+'_open')) {
+			$this._menuToggle();
+		}
+	}
+	
+	$.fn[mobileMenu] = function ( options ) {
+		var args = arguments;
+
+		// Is the first parameter an object (options), or was omitted, instantiate a new instance
+		if (options === undefined || typeof options === 'object') {
+			return this.each(function () {
+
+				// Only allow the plugin to be instantiated once due to methods
+				if (!$.data(this, 'plugin_' + mobileMenu)) {
+
+					// if it has no instance, create a new one, pass options to our plugin constructor,
+					// and store the plugin instance in the elements jQuery data object.
+					$.data(this, 'plugin_' + mobileMenu, new Plugin( this, options ));
+				}
+			});
+
+		// If is a string and doesn't start with an underscore or 'init' function, treat this as a call to a public method.
+		} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
+
+			// Cache the method call to make it possible to return a value
+			var returns;
+
+			this.each(function () {
+				var instance = $.data(this, 'plugin_' + mobileMenu);
+
+				// Tests that there's already a plugin-instance and checks that the requested public method exists
+				if (instance instanceof Plugin && typeof instance[options] === 'function') {
+
+					// Call the method of our plugin instance, and pass it the supplied arguments.
+					returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
+				}
+			});
+
+			// If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability.
+			return returns !== undefined ? returns : this;
+		}
+	};
+}(jQuery, document, window));
+
+(function($) {
+
+// Create the responsive menu using SlickNav.
+Drupal.admin.behaviors.responsivemenu = function (context, settings, $adminMenu) {
+
+    $('#admin-menu-menu-responsive').slicknav({
+		label: 'Menu',
+		prependTo:'body',
+		closedSymbol: "<i class=\"closed\"></i>",
+		openedSymbol: "<i class=\"open\"></i>",
+		allowParentLinks: true
+	});
+
+};
+
+// Create the responsive shortcuts dropdown.
+Drupal.admin.behaviors.responsiveshortcuts = function (context, settings, $adminMenu) {
+
+  // Check if there are any shortucts to respondify.
+  if(jQuery("div.toolbar-shortcuts ul.menu li").length){
+
+	  // Create the dropdown base
+	  $("<select id='responsive-shortcuts-dropdown'/>").appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts");
+
+	  // Create default option "Select"
+	  $("<option />", {
+	    "selected"  :  "selected",
+	    "class"     :  "hide",
+	    "value"     :  "",
+	    "text"      :  Drupal.t('Shortcuts')
+	  }).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
+
+	  // Populate dropdown with menu items
+	  $("#admin-menu-shortcuts-responsive div.toolbar-shortcuts a").each(function() {
+	    var el = $(this);
+	    $("<option />", {
+	      "value"   :  el.attr("href"),
+	      "text"    :  el.text()
+	    }).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
+	  });
+
+      // Redirect the user when selecting an option.
+	  $("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select").change(function() {
+	    window.location = $(this).find("option:selected").val();
+	  });
+
+	  // Clean the mess.
+	  $('#admin-menu-shortcuts-responsive div.toolbar-shortcuts ul').remove();
+	  // Move the select box into the responsive menu.
+	  $("#admin-menu-shortcuts-responsive").prependTo(".slicknav_menu");
+
+	  }
+
+  // Remove the edit shortcuts link from the DOM to avoid duble rendering.
+  $('#admin-menu-shortcuts-responsive #edit-shortcuts').remove();
+
+};
+})(jQuery);

+ 256 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/js/slicknav/slicknav.css

@@ -0,0 +1,256 @@
+/*
+    Mobile Menu Core Style
+*/
+
+.slicknav_btn { position: relative; display: block; vertical-align: middle; float: right; padding: 0.438em 0.625em 0.438em 0.625em; line-height: 1.125em; cursor: pointer; }
+.slicknav_menu  .slicknav_menutxt { display: block; line-height: 1.188em; float: left; }
+.slicknav_menu .slicknav_icon { float: left; margin: 0.188em 0 0 0.438em; }
+.slicknav_menu .slicknav_no-text { margin: 0 }
+.slicknav_menu .slicknav_icon-bar { display: block; width: 1.125em; height: 0.125em; -webkit-border-radius: 1px; -moz-border-radius: 1px; border-radius: 1px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); }
+.slicknav_btn .slicknav_icon-bar + .slicknav_icon-bar { margin-top: 0.188em }
+.slicknav_nav { clear: both }
+.slicknav_nav ul,
+.slicknav_nav li { display: block }
+.slicknav_nav .slicknav_arrow { font-size: 0.8em; margin: 0 0 0 0.4em; }
+.slicknav_nav .slicknav_item { display: block; cursor: pointer; }
+.slicknav_nav a { display: block }
+.slicknav_nav .slicknav_item a { display: inline }
+.slicknav_menu:before,
+.slicknav_menu:after { content: " "; display: table; }
+.slicknav_menu:after { clear: both }
+/* IE6/7 support */
+.slicknav_menu { *zoom: 1 }
+
+/* 
+    User Default Style
+    Change the following styles to modify the appearance of the menu.
+*/
+
+.slicknav_menu {
+	font-size:16px;
+}
+/* Button */
+.slicknav_btn {
+	margin: 5px 5px 6px;	
+	text-decoration:none;	
+	text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);	
+	-webkit-border-radius: 4px;
+	-moz-border-radius: 4px;
+	border-radius: 4px;  
+	background-color: #222222;
+}
+/* Button Text */
+.slicknav_menu  .slicknav_menutxt {	
+	color: #FFF;
+	font-weight: bold;	
+	text-shadow: 0 1px 3px #000;	
+}
+/* Button Lines */
+.slicknav_menu .slicknav_icon-bar {
+  background-color: #f5f5f5;
+}
+.slicknav_menu {
+	background:#4c4c4c;
+	padding:5px;
+}
+.slicknav_nav {
+	color:#fff;
+	margin:0;	
+	padding:0;
+	font-size:0.875em;
+}
+.slicknav_nav, .slicknav_nav ul {
+	list-style: none;
+	overflow:hidden;
+}
+.slicknav_nav ul {
+	padding:0;
+	margin:0 0 0 20px;
+}
+.slicknav_nav .slicknav_item {
+	padding:5px 10px;
+	margin:2px 5px;
+}
+.slicknav_nav a{
+	padding:5px 10px;
+	margin:2px 5px;
+	text-decoration:none;
+	color:#fff;
+}
+.slicknav_nav .slicknav_item a {
+	padding:0;
+	margin:0;
+}
+.slicknav_nav .slicknav_item:hover {
+	-webkit-border-radius: 6px;
+	-moz-border-radius: 6px;
+	border-radius: 6px;
+	background:#ccc;
+	color:#fff;	
+}
+.slicknav_nav a:hover{
+	-webkit-border-radius: 6px;
+	-moz-border-radius: 6px;
+	border-radius: 6px;
+	background:#ccc;
+	color:#222;
+}
+.slicknav_nav .slicknav_txtnode {
+     margin-left:15px;   
+}
+
+/* ADMiNiMAL MENU STYLING */
+.slicknav_menu {
+	display:none;
+}
+
+#admin-menu-menu-responsive {
+	display: none;
+}
+
+@media screen and (max-width: 1024px) {
+	body.admin-menu.adminimal-menu:before {
+		display: none;
+	}
+	#admin-menu-menu, a#edit-shortcuts, li.admin-menu-account, #admin-menu-search, #admin-menu-users, {
+		display:none;
+	}
+
+	#admin-menu {
+		display: none !important;
+	}
+
+	body.admin-menu, body.admin-menu.adminimal-menu.adminimal-backend, body.admin-menu.adminimal-menu.adminimal-frontend {
+		margin-top: 0px !important;
+	}
+
+	.slicknav_menu {
+		display:block;
+	}
+
+	#admin-menu-shortcuts-responsive {
+	    background: url("../../images/circledown.svg") no-repeat scroll 16px 50% transparent;
+	    float: right;
+	}
+
+	#admin-menu-shortcuts-responsive select {
+	    -moz-appearance: none;
+	    -webkit-appearance: none;
+	    text-indent: 0.01px;
+	    text-overflow: '';
+	    border: none;
+	    font-size: 0px;
+	    background: transparent;
+	    height: 46px;
+	    width: 46px;
+	    display: block;
+	}
+
+	#admin-menu-shortcuts-responsive select option {
+	    font-size: 14px;
+	    background: #fff;
+	    color: #333;
+	}
+
+}
+
+.slicknav_btn {
+	float: left;
+}
+
+.slicknav_menu {
+    background: none repeat scroll 0 0 #222222;
+    padding: 0;
+    font-family: Open Sans, "Segoe UI", "Helvetica", sans-serif;
+    color: color: #FFFFFF;
+}
+
+.slicknav_menu a, .slicknav_menu a:link, .slicknav_menu a:active {
+	color: #fff !important;
+	text-decoration: none !important;
+}
+
+.slicknav_menu:after {
+    clear: none;
+}
+
+.slicknav_btn {
+    margin: 0;
+    outline: medium none;
+    padding: 5px 10px;
+    text-shadow: none;
+    border-radius: 0;
+}
+
+.slicknav_icon {
+	display: none;
+}
+
+.slicknav_menu .slicknav_menutxt {
+	text-shadow: none;
+	font-weight: normal;
+}
+
+.slicknav_nav a:hover, .slicknav_nav .slicknav_item:hover {
+	border-radius: 0;
+}
+
+.slicknav_nav li.expandable, #admin-menu .slicknav_nav .dropdown li {
+	float: none !important;
+}
+
+.slicknav_nav .slicknav_item {
+	
+}
+
+.slicknav_btn {
+    background: url("../../images/menu.svg") no-repeat scroll 8px 16px rgba(0, 0, 0, 0);
+    font-size: 23px;
+    padding: 10px 34px;
+}
+
+.slicknav_menu:after {
+    clear: both;
+}
+
+.slicknav_nav .slicknav_item:hover, .slicknav_nav a:hover {
+    background: none repeat scroll 0 0 #0074BD;
+    border-radius: 0;
+    color: #FFFFFF;
+    text-decoration: none !important;
+}
+
+.slicknav_nav .slicknav_arrow i {
+    display: inline-block;
+    background: url("../../images/menu-arrows.svg") no-repeat scroll 0px 3px rgba(0, 0, 0, 0);
+    font-size: 0.8em;
+    height: 16px;
+    margin: 0 0 0 0.4em;
+    width: 16px;
+}
+
+.slicknav_open .slicknav_arrow i.open {
+	background: url("../../images/menu-arrows.svg") no-repeat scroll -37px 0 rgba(0, 0, 0, 0);
+}
+
+.slicknav_nav li.admin-menu-toolbar-home-menu {
+	background-color: #333;
+}
+
+li.admin-menu-icon.slicknav_parent > a {
+	padding: 0;
+	margin: 0;
+}
+
+li.admin-menu-icon.slicknav_parent > a > span.slicknav_arrow {
+	position: absolute;
+	margin-top: 10px;
+	margin-left: -5px;
+}
+
+.slicknav_nav li.admin-menu-toolbar-home-menu span.admin-menu-home-icon {
+    background: url("../../images/house.svg") no-repeat scroll 17px 10px rgba(0, 0, 0, 0);
+    display: inline-block;
+    height: 40px;
+    width: 50px;
+}

+ 18 - 0
sites/all/modules/contrib/admin/adminimal_admin_menu/updates/update_7100.php

@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Adminimal Menu Update file 7100.
+ */
+
+// Define Adminimal Menu path.
+$module_path = drupal_get_path('module', 'adminimal_admin_menu');
+
+// Delete the "adminimal_admin_menu.js" file.
+file_unmanaged_delete($module_path . '/adminimal_admin_menu.js');
+
+// Empty the "slicknav" folder.
+file_unmanaged_delete_recursive($module_path . '/slicknav');
+
+// Delete the "slicknav" folder.
+drupal_rmdir($module_path . '/slicknav');

+ 0 - 0
sites/all/modules/contrib/admin/content_type_extras/LICENSE.txt


+ 3 - 3
sites/all/modules/contrib/admin/content_type_extras/content_type_extras.info

@@ -6,9 +6,9 @@ configure = admin/structure/types/defaults
 
 
 files[] = includes/content_type_extras.node_type_form.inc
 files[] = includes/content_type_extras.node_type_form.inc
 
 
-; Information added by Drupal.org packaging script on 2014-04-11
-version = "7.x-1.8"
+; Information added by Drupal.org packaging script on 2016-09-02
+version = "7.x-1.12"
 core = "7.x"
 core = "7.x"
 project = "content_type_extras"
 project = "content_type_extras"
-datestamp = "1397230455"
+datestamp = "1472853844"
 
 

+ 87 - 40
sites/all/modules/contrib/admin/content_type_extras/content_type_extras.module

@@ -50,7 +50,9 @@ function content_type_extras_preprocess_page(&$vars) {
   }
   }
 
 
   elseif (!empty($vars['node'])) {
   elseif (!empty($vars['node'])) {
-    $hide_title = content_type_extras_get_setting('content_type_extras_title_hide', $vars['node']->type);
+    // Ran into an issue when using the Print module where the node type isn't set, throwing an error
+    // so we are checking to see if the node type is set first
+    $hide_title = isset($vars['node']->type) ? content_type_extras_get_setting('content_type_extras_title_hide', $vars['node']->type) : '';
     if ($hide_title) {
     if ($hide_title) {
       $hide_title_css = content_type_extras_get_default('content_type_extras_title_hide_css');
       $hide_title_css = content_type_extras_get_default('content_type_extras_title_hide_css');
       if ($hide_title_css) {
       if ($hide_title_css) {
@@ -142,6 +144,8 @@ function content_type_extras_form_alter(&$form, &$form_state, $form_id) {
       $form['title']['#title'] = $title_label;
       $form['title']['#title'] = $title_label;
     }
     }
 
 
+    $form['actions']['submit']['#value'] = content_type_extras_get_setting('content_type_extras_save_button', $type);
+
     $save_and_new = content_type_extras_get_setting('content_type_extras_save_and_new', $type);
     $save_and_new = content_type_extras_get_setting('content_type_extras_save_and_new', $type);
     if (!empty($save_and_new)) {
     if (!empty($save_and_new)) {
       $form['actions']['save_and_new'] = array(
       $form['actions']['save_and_new'] = array(
@@ -172,10 +176,26 @@ function content_type_extras_form_alter(&$form, &$form_state, $form_id) {
     if (!empty($preview)) {
     if (!empty($preview)) {
       $form['actions']['preview']['#value']  = content_type_extras_get_setting('content_type_extras_preview_button', $type);
       $form['actions']['preview']['#value']  = content_type_extras_get_setting('content_type_extras_preview_button', $type);
     }
     }
+    elseif (isset($form['actions']['preview'])) {
+      unset($form['actions']['preview']);
+    }
 
 
     $cancel = content_type_extras_get_setting('content_type_extras_cancel', $type);
     $cancel = content_type_extras_get_setting('content_type_extras_cancel', $type);
     if (!empty($cancel)) {
     if (!empty($cancel)) {
-      drupal_add_js(drupal_get_path('module', 'content_type_extras') . '/js/content_type_extras.cancel_button.js');
+      $cancel_hide_warning = content_type_extras_get_setting('content_type_extras_cancel_hide_warning', $type);
+      $cancel_location = content_type_extras_get_setting('content_type_extras_cancel_button_location', $type);
+      $location_path = content_type_extras_get_setting('content_type_extras_cancel_button_location_path', $type);
+
+      $form['#attached']['js'][] = drupal_get_path('module', 'content_type_extras') . '/js/content_type_extras.cancel_button.js';
+      $form['#attached']['js'][] = array(
+        'data' => array('content_type_extras' => array(
+          'hide_warning' => $cancel_hide_warning,
+          'cancel_location' => $cancel_location,
+          'location_path' => $location_path,
+        )),
+        'type' => 'setting',
+      );
+
       $form['actions']['cancel'] = array(
       $form['actions']['cancel'] = array(
         '#type'        => 'button',
         '#type'        => 'button',
         '#value'       => t('Cancel'),
         '#value'       => t('Cancel'),
@@ -186,8 +206,9 @@ function content_type_extras_form_alter(&$form, &$form_state, $form_id) {
 
 
     // Add the form buttons to the top of the page
     // Add the form buttons to the top of the page
     // Based on: http://blog.urbaninsight.com/2011/09/20/editors-perspective-creating-content-drupal
     // Based on: http://blog.urbaninsight.com/2011/09/20/editors-perspective-creating-content-drupal
-    $top_buttons = content_type_extras_get_default('content_type_extras_top_buttons');
-    if (!empty($top_buttons['node_edit'])) {
+    $top_buttons = content_type_extras_get_setting('content_type_extras_top_buttons', $type);
+
+    if (!empty($top_buttons['node_edit']) && in_array('node_edit', $top_buttons)) {
       $form['pre_actions'] = $form['actions'];
       $form['pre_actions'] = $form['actions'];
       $form['pre_actions']['#weight'] = -100;
       $form['pre_actions']['#weight'] = -100;
     }
     }
@@ -199,16 +220,20 @@ function content_type_extras_form_alter(&$form, &$form_state, $form_id) {
   // but the user can enter a name any length, getting an error message if the name is too long
   // but the user can enter a name any length, getting an error message if the name is too long
   // That drives me crazy!! This fixes that!
   // That drives me crazy!! This fixes that!
   elseif ($form_id == 'field_ui_field_overview_form') {
   elseif ($form_id == 'field_ui_field_overview_form') {
+    $type = arg(4);
+
     drupal_add_js(drupal_get_path('module', 'content_type_extras') . '/js/content_type_extras.manage_fields.js');
     drupal_add_js(drupal_get_path('module', 'content_type_extras') . '/js/content_type_extras.manage_fields.js');
 
 
-    $top_buttons = content_type_extras_get_default('content_type_extras_top_buttons');
-    if (!empty($top_buttons['manage_fields'])) {
+    $top_buttons = content_type_extras_get_setting('content_type_extras_top_buttons', $type);
+    if (!empty($top_buttons['manage_fields']) || in_array('manage_fields', $top_buttons)) {
       $form['pre_actions'] = $form['actions'];
       $form['pre_actions'] = $form['actions'];
       $form['pre_actions']['#weight'] = -100;
       $form['pre_actions']['#weight'] = -100;
     }
     }
 
 
-    $form['fields']['_add_new_field']['field_name']['#maxlength'] = 26;
-    $form['fields']['_add_new_field']['field_name']['#description'] .= t(' - <span class="characters">26</span> characters max.');
+    if (isset($form['fields']['_add_new_field'])) {
+      $form['fields']['_add_new_field']['field_name']['#maxlength'] = 26;
+      $form['fields']['_add_new_field']['field_name']['#description'] .= t(' - <span class="characters">26</span> characters max.');
+    }
     // The field_group module does the same thing, so if that is enabled, handle it the same way
     // The field_group module does the same thing, so if that is enabled, handle it the same way
     if (module_exists('field_group')) {
     if (module_exists('field_group')) {
       $form['fields']['_add_new_group']['group_name']['#maxlength'] = 26;
       $form['fields']['_add_new_group']['group_name']['#maxlength'] = 26;
@@ -238,8 +263,30 @@ function content_type_extras_node_form_new_submit(&$form, &$form_state) {
  * Form submission for $form_id *_node_form
  * Form submission for $form_id *_node_form
  */
  */
 function content_type_extras_node_form_edit_submit(&$form, &$form_state) {
 function content_type_extras_node_form_edit_submit(&$form, &$form_state) {
+  $query_string = array();
+  // Allow compatibility with content lock module.
+  if (isset($_REQUEST['content_lock_token'])) {
+    $query_string['content_lock_token'] = $_REQUEST['content_lock_token'];
+  }
+  // Allow compatibility with additional query parameters.
+  $query_parameters = drupal_get_query_parameters();
+  if (!empty($query_parameters)) {
+    foreach ($query_parameters as $key => $value) {
+      $query_string[$key] = $value;
+    }
+  }
+  if (!empty($query_string)) {
+    $form_state['redirect'] = url('node/' . $form_state['values']['nid'] . '/edit',
+      array(
+        'query' => $query_string,
+        'absolute' => TRUE,
+      )
+    );
+  }
+  else {
+    $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/edit';
+  }
   unset($_GET['destination']);
   unset($_GET['destination']);
-  $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/edit';
 }
 }
 
 
 /**
 /**
@@ -319,23 +366,16 @@ function content_type_extras_get_setting($setting, $type) {
  */
  */
 function content_type_extras_get_default($setting = NULL) {
 function content_type_extras_get_default($setting = NULL) {
   // This has to be unserialized as it will crash the default settings page.
   // This has to be unserialized as it will crash the default settings page.
-  $defaults = variable_get('content_type_extras_default_settings');
+  $defaults = variable_get('content_type_extras_default_settings', array()) + content_type_extras_get_initial();
 
 
+  // Return all default settings.
   if ($setting == NULL) {
   if ($setting == NULL) {
-    if (!empty($defaults)) {
-      return $defaults;
-    }
-    else {
-      return content_type_extras_get_initial();
-    }
+    return $defaults;
   }
   }
-  else {
-    if (isset($defaults[$setting])) {
-      return $defaults[$setting];
-    }
-    else {
-      return content_type_extras_get_initial($setting);
-    }
+
+  // Return specific default setting.
+  else if (isset($defaults[$setting])) {
+    return $defaults[$setting];
   }
   }
 }
 }
 
 
@@ -362,20 +402,25 @@ function content_type_extras_get_initial($setting = NULL) {
     ),
     ),
     'node_submitted' => 1,
     'node_submitted' => 1,
     // Values set by content_type_extras.module
     // Values set by content_type_extras.module
-    'content_type_extras_preview_button'          => t('Preview'),
-    'content_type_extras_save_and_new'            => 0,
-    'content_type_extras_save_and_new_button'     => t('Save and New'),
-    'content_type_extras_save_and_edit'           => 0,
-    'content_type_extras_save_and_edit_button'    => t('Save and Edit'),
-    'content_type_extras_cancel'                  => 0,
-    'content_type_extras_title_hide'              => 0,
-    'content_type_extras_title_hide_css'          => 0,
-    'content_type_extras_title_hide_front'        => 0,
-    'content_type_extras_top_buttons'             => array(),
-    'content_type_extras_remove_body'             => 0,
-    'content_type_extras_descriptions_required'   => 0,
-    'content_type_extras_user_permissions_select' => 'cte',
-    'content_type_extras_excluded_node_forms'     => array(),
+    'content_type_extras_save_button'                 => t('Save'),
+    'content_type_extras_preview_button'              => t('Preview'),
+    'content_type_extras_save_and_new'                => 0,
+    'content_type_extras_save_and_new_button'         => t('Save and New'),
+    'content_type_extras_save_and_edit'               => 0,
+    'content_type_extras_save_and_edit_button'        => t('Save and Edit'),
+    'content_type_extras_cancel'                      => 0,
+    'content_type_extras_cancel_hide_warning'         => 0,
+    'content_type_extras_title_hide'                  => 0,
+    'content_type_extras_title_hide_css'              => 0,
+    'content_type_extras_title_hide_front'            => 0,
+    'content_type_extras_top_buttons'                 => array(),
+    'content_type_extras_remove_body'                 => 0,
+    'content_type_extras_disable_token_display'       => 0,
+    'content_type_extras_cancel_button_location'      => 'previous',
+    'content_type_extras_cancel_button_location_path' => '',
+    'content_type_extras_descriptions_required'       => 0,
+    'content_type_extras_user_permissions_select'     => 'cte',
+    'content_type_extras_excluded_node_forms'         => array(),
     // Values set by comment.module
     // Values set by comment.module
     'comment' => array(
     'comment' => array(
       'comment'          => 2,
       'comment'          => 2,
@@ -408,9 +453,11 @@ function content_type_extras_get_initial($setting = NULL) {
   $admin_role = variable_get('user_admin_role', '');
   $admin_role = variable_get('user_admin_role', '');
 
 
   $initial_values['user_permissions'] = array(
   $initial_values['user_permissions'] = array(
-    'create_roles' => array($admin_role => $admin_role),
-    'edit_roles'   => array($admin_role => $admin_role),
-    'delete_roles' => array($admin_role => $admin_role),
+    'create_roles'     => array($admin_role => $admin_role),
+    'edit_roles'       => array($admin_role => $admin_role),
+    'delete_roles'     => array($admin_role => $admin_role),
+    'edit_own_roles'   => array($admin_role => $admin_role),
+    'delete_own_roles' => array($admin_role => $admin_role),
   );
   );
 
 
   if ($setting == NULL) {
   if ($setting == NULL) {

+ 52 - 7
sites/all/modules/contrib/admin/content_type_extras/includes/content_type_extras.admin.inc

@@ -5,11 +5,17 @@ function content_type_extras_settings() {
   // Get all available user roles
   // Get all available user roles
   $roles = user_roles();
   $roles = user_roles();
   $admin_role = variable_get('user_admin_role', 0);
   $admin_role = variable_get('user_admin_role', 0);
-  
+
   if ($admin_role != 0) {
   if ($admin_role != 0) {
     $roles[$admin_role] .= t(' <em>(administrator role)</em>');
     $roles[$admin_role] .= t(' <em>(administrator role)</em>');
   }
   }
-  
+  if(!isset($defaults['user_permissions']['edit_own_roles'])) {
+    $defaults['user_permissions']['edit_own_roles'] = array();
+  }
+  if(!isset($defaults['user_permissions']['delete_own_roles'])) {
+    $defaults['user_permissions']['delete_own_roles'] = array();
+  }
+
   $form = array(
   $form = array(
     'intro' => array(
     'intro' => array(
       '#markup' => t('Set the default settings for all new content types below. These settings can be overridden on the content type page.'),
       '#markup' => t('Set the default settings for all new content types below. These settings can be overridden on the content type page.'),
@@ -137,12 +143,24 @@ function content_type_extras_settings() {
           '#options'       => $roles,
           '#options'       => $roles,
           '#default_value' => $defaults['user_permissions']['edit_roles'],
           '#default_value' => $defaults['user_permissions']['edit_roles'],
         ),
         ),
+        'edit_own_roles' => array(
+          '#type'          => 'checkboxes',
+          '#title'         => t('Roles that can EDIT own content'),
+          '#options'       => $roles,
+          '#default_value' => $defaults['user_permissions']['edit_own_roles'],
+        ),
         'delete_roles' => array(
         'delete_roles' => array(
           '#type'          => 'checkboxes',
           '#type'          => 'checkboxes',
           '#title'         => t('Roles that can DELETE any content'),
           '#title'         => t('Roles that can DELETE any content'),
           '#options'       => $roles,
           '#options'       => $roles,
           '#default_value' => $defaults['user_permissions']['delete_roles'],
           '#default_value' => $defaults['user_permissions']['delete_roles'],
         ),
         ),
+        'delete_own_roles' => array(
+          '#type'          => 'checkboxes',
+          '#title'         => t('Roles that can DELETE own content'),
+          '#options'       => $roles,
+          '#default_value' => $defaults['user_permissions']['delete_own_roles'],
+        ),
       ),
       ),
       'extras' => array(
       'extras' => array(
         '#type'   => 'fieldset',
         '#type'   => 'fieldset',
@@ -164,6 +182,33 @@ function content_type_extras_settings() {
             '#title'         => t('Remove body field from content types'),
             '#title'         => t('Remove body field from content types'),
             '#default_value' => $defaults['content_type_extras_remove_body'],
             '#default_value' => $defaults['content_type_extras_remove_body'],
           ),
           ),
+          'content_type_extras_disable_token_display' => array(
+            '#type'          => 'checkbox',
+            '#description'   => t('Using modules such as pathauto uses tokens and, by default, we show the replacement patterns to use available tokens. However, this can slow some page loads down considerably. To disable token replacement patterns from being displayed on pages altered by Content Type: Extras (i.e. the content type edit page) check this box. This feature only hides the token replacement patterns. Tokens may still be used, they just must be entered manually.'),
+            '#title'         => t('Disable token replacement patterns'),
+            '#default_value' => $defaults['content_type_extras_disable_token_display'],
+          ),
+          'content_type_extras_cancel_button_location' => array(
+            '#type'          => 'radios',
+            '#description'   => t('Choose whether the user will be taken to the previous page or a specific page when the Cancel button is pressed.'),
+            '#title'         => t('Cancel button destination'),
+            '#options'       => array(
+              'previous' => t('Previous page'),
+              'static_path' => t('Static path'),
+            ),
+            '#default_value' => $defaults['content_type_extras_cancel_button_location'],
+          ),
+          'content_type_extras_cancel_button_location_path' => array(
+            '#type'          => 'textfield',
+            '#description'   => t('Set the path where the user should be redirected when the cancel button is pressed. <strong>Note:</strong> In most cases you will probably want to prepend the path with a /.'),
+            '#title'         => t('Path'),
+            '#default_value' => $defaults['content_type_extras_cancel_button_location_path'],
+            '#states'        => array(
+              'visible' => array(
+                ':input[name="content_type_extras_cancel_button_location"]' => array('value' => 'static_path'),
+              ),
+            ),
+          ),
         ),
         ),
         'node_titles' => array(
         'node_titles' => array(
           '#type'   => 'fieldset',
           '#type'   => 'fieldset',
@@ -217,7 +262,7 @@ function content_type_extras_settings() {
       ),
       ),
     ),
     ),
   );
   );
-  
+
   if (module_exists('comment')) {
   if (module_exists('comment')) {
     $form['content_type_extras']['comment'] = array(
     $form['content_type_extras']['comment'] = array(
       '#type'   => 'fieldset',
       '#type'   => 'fieldset',
@@ -279,7 +324,7 @@ function content_type_extras_settings() {
       ),
       ),
     );
     );
   }
   }
-  
+
   if (module_exists('xmlsitemap')) {
   if (module_exists('xmlsitemap')) {
     $form['content_type_extras']['xmlsitemap_settings'] = array(
     $form['content_type_extras']['xmlsitemap_settings'] = array(
       '#type'   => 'fieldset',
       '#type'   => 'fieldset',
@@ -376,14 +421,14 @@ function content_type_extras_settings() {
       '#default_value' => $defaults['content_type_extras_user_permissions_select'],
       '#default_value' => $defaults['content_type_extras_user_permissions_select'],
     );
     );
   }
   }
-  
+
   $form['actions'] = array(
   $form['actions'] = array(
     'submit'   => array(
     'submit'   => array(
       '#type'  => 'submit',
       '#type'  => 'submit',
       '#value' => t('Save configuration'),
       '#value' => t('Save configuration'),
     ),
     ),
   );
   );
-  
+
   return $form;
   return $form;
 }
 }
 
 
@@ -391,7 +436,7 @@ function content_type_extras_settings_submit(&$form, &$form_state) {
   $values = $form_state['values'];
   $values = $form_state['values'];
   // Place the values of content_type_extras_excluded_node_forms into an array
   // Place the values of content_type_extras_excluded_node_forms into an array
   $values['content_type_extras_excluded_node_forms'] = explode("\n", $values['content_type_extras_excluded_node_forms']);
   $values['content_type_extras_excluded_node_forms'] = explode("\n", $values['content_type_extras_excluded_node_forms']);
-  
+
   unset($values['submit'], $values['form_build_id'], $values['form_token'], $values['form_id'], $values['op']);
   unset($values['submit'], $values['form_build_id'], $values['form_token'], $values['form_id'], $values['op']);
   variable_set('content_type_extras_default_settings', $values);
   variable_set('content_type_extras_default_settings', $values);
 
 

+ 131 - 60
sites/all/modules/contrib/admin/content_type_extras/includes/content_type_extras.node_type_form.inc

@@ -9,12 +9,12 @@ function content_type_extras_node_type_form(&$form) {
 
 
   // We need to set the weights of the 'Preview' button radios
   // We need to set the weights of the 'Preview' button radios
   $form['submission']['node_preview']['#weight'] = 2;
   $form['submission']['node_preview']['#weight'] = 2;
-  
+
   $type = $form['type']['#default_value'];
   $type = $form['type']['#default_value'];
-  
+
   // We need to check whether the description field should be required or not.
   // We need to check whether the description field should be required or not.
   $form['description']['#required'] = content_type_extras_get_setting('content_type_extras_descriptions_required', $type);
   $form['description']['#required'] = content_type_extras_get_setting('content_type_extras_descriptions_required', $type);
-  
+
   // Add Preview button text option
   // Add Preview button text option
   $form['submission']['content_type_extras_preview_button'] = array(
   $form['submission']['content_type_extras_preview_button'] = array(
     '#type'          => 'textfield',
     '#type'          => 'textfield',
@@ -28,7 +28,15 @@ function content_type_extras_node_type_form(&$form) {
       ),
       ),
     ),
     ),
   );
   );
-  
+
+  $form['submission']['content_type_extras_save_button'] = array(
+    '#type'          => 'textfield',
+    '#title'         => "'Save' button value",
+    '#description'   => t(''),
+    '#default_value' => content_type_extras_get_setting('content_type_extras_save_button', $type),
+    '#weight'        => $form['submission']['node_preview']['#weight'] - 1,
+  );
+
   // Add the option to have a "Save and New" button added to the content type
   // Add the option to have a "Save and New" button added to the content type
   $form['submission']['content_type_extras_save_and_new'] = array(
   $form['submission']['content_type_extras_save_and_new'] = array(
     '#type'        => 'radios',
     '#type'        => 'radios',
@@ -53,7 +61,7 @@ function content_type_extras_node_type_form(&$form) {
       ),
       ),
     ),
     ),
   );
   );
-  
+
   // Add the option to have a "Save and Edit" button added to the content type
   // Add the option to have a "Save and Edit" button added to the content type
   $form['submission']['content_type_extras_save_and_edit'] = array(
   $form['submission']['content_type_extras_save_and_edit'] = array(
     '#type'        => 'radios',
     '#type'        => 'radios',
@@ -78,7 +86,7 @@ function content_type_extras_node_type_form(&$form) {
       ),
       ),
     ),
     ),
   );
   );
-  
+
   // Add the option to have a "Cancel" button added to the content type
   // Add the option to have a "Cancel" button added to the content type
   $form['submission']['content_type_extras_cancel'] = array(
   $form['submission']['content_type_extras_cancel'] = array(
     '#type'        => 'radios',
     '#type'        => 'radios',
@@ -91,20 +99,33 @@ function content_type_extras_node_type_form(&$form) {
     '#default_value' => content_type_extras_get_setting('content_type_extras_cancel', $type),
     '#default_value' => content_type_extras_get_setting('content_type_extras_cancel', $type),
     '#weight'        => $form['submission']['node_preview']['#weight'] + 6,
     '#weight'        => $form['submission']['node_preview']['#weight'] + 6,
   );
   );
-  
+  // Add the option to have the warning message appear when using the "Cancel" button
+  $form['submission']['content_type_extras_cancel_hide_warning'] = array(
+    '#type'        => 'checkbox',
+    '#title'       => 'Hide cancel button warning message',
+    '#description' => t('If checked, a javascript alert box will <strong>not</strong> display warning the user that their changes will not be saved.'),
+    '#default_value' => content_type_extras_get_setting('content_type_extras_cancel_hide_warning', $type),
+    '#weight'        => $form['submission']['content_type_extras_cancel']['#weight'] + 1,
+    '#states'        => array(
+      'visible' => array(
+        'input[name=content_type_extras_cancel]' => array('value' => '1'),
+      ),
+    ),
+  );
+
   // Set weight of help text area
   // Set weight of help text area
   $form['submission']['help']['#weight'] = $form['submission']['node_preview']['#weight'] + 7;
   $form['submission']['help']['#weight'] = $form['submission']['node_preview']['#weight'] + 7;
-  
+
   // Set 'Title field label'
   // Set 'Title field label'
   $form['submission']['title_label']['#default_value'] = content_type_extras_get_setting('title_label', $type);
   $form['submission']['title_label']['#default_value'] = content_type_extras_get_setting('title_label', $type);
   // Set 'Preview' button default
   // Set 'Preview' button default
   $form['submission']['node_preview']['#default_value'] = content_type_extras_get_setting('node_preview', $type);
   $form['submission']['node_preview']['#default_value'] = content_type_extras_get_setting('node_preview', $type);
   // Set 'Display author and date information.'
   // Set 'Display author and date information.'
   $form['display']['node_submitted']['#default_value'] = content_type_extras_get_setting('node_submitted', $type);
   $form['display']['node_submitted']['#default_value'] = content_type_extras_get_setting('node_submitted', $type);
-  
+
   // Set 'Publishing options'
   // Set 'Publishing options'
   $form['workflow']['node_options']['#default_value'] = content_type_extras_get_setting('node_options', $type);
   $form['workflow']['node_options']['#default_value'] = content_type_extras_get_setting('node_options', $type);
-  
+
   if (module_exists('comment')) {
   if (module_exists('comment')) {
     // A new content type is being added
     // A new content type is being added
     if (empty($form['#node_type']->name)) {
     if (empty($form['#node_type']->name)) {
@@ -118,25 +139,28 @@ function content_type_extras_node_type_form(&$form) {
       $form['comment']['comment_preview']['#default_value'] = $comment_settings['preview'];
       $form['comment']['comment_preview']['#default_value'] = $comment_settings['preview'];
     }
     }
   }
   }
-  
-  if (module_exists('xmlsitemap')) {
-    // XML Sitemap stores its variables a little differently, so we have to adjust for it here.
-    $xmlsitemap_settings = content_type_extras_get_setting('xmlsitemap_settings', 'node_' . $type);
-    $form['xmlsitemap']['status']['#default_value'] = $xmlsitemap_settings['status'];
-    $form['xmlsitemap']['priority']['#default_value'] = $xmlsitemap_settings['priority'];
-  }
 
 
-  if (module_exists('scheduler')) {
-    // Scheduler stores its variables a little differently, so we have to adjust for it here.
-    $scheduler_settings = content_type_extras_get_setting('scheduler_settings', 'node_' . $type);
-    
-    $form['scheduler']['publish']['scheduler_publish_enable']['#default_value'] = $scheduler_settings['publish_enable'];
-    $form['scheduler']['publish']['scheduler_publish_touch']['#default_value'] = $scheduler_settings['publish_touch'];
-    $form['scheduler']['publish']['scheduler_publish_require']['#default_value'] = $scheduler_settings['publish_require'];
-    $form['scheduler']['publish']['scheduler_publish_revision']['#default_value'] = $scheduler_settings['publish_revision'];
-    $form['scheduler']['unpublish']['scheduler_unpublish_enable']['#default_value'] = $scheduler_settings['unpublish_enable'];
-    $form['scheduler']['unpublish']['scheduler_unpublish_require']['#default_value'] = $scheduler_settings['unpublish_require'];
-    $form['scheduler']['unpublish']['scheduler_unpublish_revision']['#default_value'] = $scheduler_settings['unpublish_revision'];
+  // A new content type is being added
+  if (empty($form['#node_type']->name)) {
+    if (module_exists('xmlsitemap')) {
+      // XML Sitemap stores its variables a little differently, so we have to adjust for it here.
+      $xmlsitemap_settings = content_type_extras_get_setting('xmlsitemap_settings', 'node_' . $type);
+      $form['xmlsitemap']['status']['#default_value'] = $xmlsitemap_settings['status'];
+      $form['xmlsitemap']['priority']['#default_value'] = $xmlsitemap_settings['priority'];
+    }
+
+    if (module_exists('scheduler')) {
+      // Scheduler stores its variables a little differently, so we have to adjust for it here.
+      $scheduler_settings = content_type_extras_get_setting('scheduler_settings', 'node_' . $type);
+
+      $form['scheduler']['publish']['scheduler_publish_enable']['#default_value'] = $scheduler_settings['publish_enable'];
+      $form['scheduler']['publish']['scheduler_publish_touch']['#default_value'] = $scheduler_settings['publish_touch'];
+      $form['scheduler']['publish']['scheduler_publish_require']['#default_value'] = $scheduler_settings['publish_require'];
+      $form['scheduler']['publish']['scheduler_publish_revision']['#default_value'] = $scheduler_settings['publish_revision'];
+      $form['scheduler']['unpublish']['scheduler_unpublish_enable']['#default_value'] = $scheduler_settings['unpublish_enable'];
+      $form['scheduler']['unpublish']['scheduler_unpublish_require']['#default_value'] = $scheduler_settings['unpublish_require'];
+      $form['scheduler']['unpublish']['scheduler_unpublish_revision']['#default_value'] = $scheduler_settings['unpublish_revision'];
+    }
   }
   }
 
 
   // Get all available user roles
   // Get all available user roles
@@ -149,28 +173,35 @@ function content_type_extras_node_type_form(&$form) {
   $create_roles = array();
   $create_roles = array();
   $edit_roles = array();
   $edit_roles = array();
   $delete_roles = array();
   $delete_roles = array();
+  $edit_own_roles = array();
+  $delete_own_roles = array();
+
   // If we are on an existing content type form
   // If we are on an existing content type form
   if (!empty($form['name']['#default_value'])) {
   if (!empty($form['name']['#default_value'])) {
     $create_roles = user_roles(FALSE, 'create ' . $form['#node_type']->type . ' content');
     $create_roles = user_roles(FALSE, 'create ' . $form['#node_type']->type . ' content');
     $edit_roles   = user_roles(FALSE, 'edit any ' . $form['#node_type']->type . ' content');
     $edit_roles   = user_roles(FALSE, 'edit any ' . $form['#node_type']->type . ' content');
     $delete_roles = user_roles(FALSE, 'delete any ' . $form['#node_type']->type . ' content');
     $delete_roles = user_roles(FALSE, 'delete any ' . $form['#node_type']->type . ' content');
+    $delete_own_roles = user_roles(FALSE, 'delete own ' . $form['#node_type']->type . ' content');
+    $edit_own_roles = user_roles(FALSE, 'edit own ' . $form['#node_type']->type . ' content');
   }
   }
   // We are creating a new content type
   // We are creating a new content type
   else {
   else {
     $user_permissions = content_type_extras_get_setting('user_permissions', '');
     $user_permissions = content_type_extras_get_setting('user_permissions', '');
-    
+
     $selected_perms = content_type_extras_get_selected_roles($user_permissions);
     $selected_perms = content_type_extras_get_selected_roles($user_permissions);
-    
+
     $create_roles = $selected_perms['create_roles'];
     $create_roles = $selected_perms['create_roles'];
     $edit_roles   = $selected_perms['edit_roles'];
     $edit_roles   = $selected_perms['edit_roles'];
+    $edit_own_roles   = $selected_perms['edit_own_roles'];
     $delete_roles = $selected_perms['delete_roles'];
     $delete_roles = $selected_perms['delete_roles'];
+    $delete_own_roles = $selected_perms['delete_own_roles'];
   }
   }
-  
+
   $permission_select = content_type_extras_get_default('content_type_extras_user_permissions_select');
   $permission_select = content_type_extras_get_default('content_type_extras_user_permissions_select');
   if ($permission_select == 'cte') {
   if ($permission_select == 'cte') {
     // We need to remove FPA's implementation
     // We need to remove FPA's implementation
     unset($form['fpa_fieldset']);
     unset($form['fpa_fieldset']);
-    
+
     $form['user_permissions'] = array(
     $form['user_permissions'] = array(
       '#type'        => 'fieldset',
       '#type'        => 'fieldset',
       '#title'       => t('User permissions'),
       '#title'       => t('User permissions'),
@@ -189,15 +220,27 @@ function content_type_extras_node_type_form(&$form) {
         '#options'       => $roles,
         '#options'       => $roles,
         '#default_value' => array_keys($edit_roles),
         '#default_value' => array_keys($edit_roles),
       ),
       ),
+      'edit_own_roles' => array(
+        '#type'          => 'checkboxes',
+        '#title'         => t('Roles that can EDIT own content of this type'),
+        '#options'       => $roles,
+        '#default_value' => array_keys($edit_own_roles),
+      ),
       'delete_roles' => array(
       'delete_roles' => array(
         '#type'          => 'checkboxes',
         '#type'          => 'checkboxes',
         '#title'         => t('Roles that can DELETE any content of this type'),
         '#title'         => t('Roles that can DELETE any content of this type'),
         '#options'       => $roles,
         '#options'       => $roles,
         '#default_value' => array_keys($delete_roles),
         '#default_value' => array_keys($delete_roles),
       ),
       ),
+        'delete_own_roles' => array(
+        '#type'          => 'checkboxes',
+        '#title'         => t('Roles that can DELETE own content of this type'),
+        '#options'       => $roles,
+        '#default_value' => array_keys($delete_own_roles),
+      ),
     );
     );
   }
   }
-  
+
   $form['extras'] = array(
   $form['extras'] = array(
     '#type'  => 'fieldset',
     '#type'  => 'fieldset',
     '#title' => t('Extra settings'),
     '#title' => t('Extra settings'),
@@ -209,8 +252,18 @@ function content_type_extras_node_type_form(&$form) {
       '#default_value' => content_type_extras_get_setting('content_type_extras_title_hide', $type),
       '#default_value' => content_type_extras_get_setting('content_type_extras_title_hide', $type),
       '#weight'        => 0,
       '#weight'        => 0,
     ),
     ),
+    'content_type_extras_top_buttons' => array(
+      '#type'        => 'checkboxes',
+      '#title'       => t('Show form buttons at top of'),
+      '#description' => t('Select the areas to duplicate form submission buttons on the top of the page.'),
+      '#options'     => array(
+        'manage_fields' => t('Manage fields form'),
+        'node_edit'     => t('Node edit form'),
+      ),
+      '#default_value' => content_type_extras_get_setting('content_type_extras_top_buttons', $type),
+    ),
   );
   );
-  
+
   if (!empty($form['#node_type']->is_new)) {
   if (!empty($form['#node_type']->is_new)) {
     // I decided to make this option only available when creating content types because:
     // I decided to make this option only available when creating content types because:
     // 1. Once the content type is created, the admin can manage the body field just like
     // 1. Once the content type is created, the admin can manage the body field just like
@@ -225,7 +278,7 @@ function content_type_extras_node_type_form(&$form) {
       '#weight'        => 10,
       '#weight'        => 10,
     );
     );
   }
   }
-  
+
   if (module_exists('pathauto')) {
   if (module_exists('pathauto')) {
     $form['extras']['pathauto_node'] = array(
     $form['extras']['pathauto_node'] = array(
       '#type'          => 'textfield',
       '#type'          => 'textfield',
@@ -234,20 +287,22 @@ function content_type_extras_node_type_form(&$form) {
       '#default_value' => content_type_extras_get_setting('pathauto_node', $type . '_pattern'),
       '#default_value' => content_type_extras_get_setting('pathauto_node', $type . '_pattern'),
       '#weight'        => 20,
       '#weight'        => 20,
     );
     );
-    
-    $form['extras']['token_help'] = array(
-      '#title'       => t('Replacement patterns'),
-      '#type'        => 'fieldset',
-      '#collapsible' => TRUE,
-      '#collapsed'   => TRUE,
-      'help' => array(
-        '#theme'       => 'token_tree',
-        '#token_types' => array('node'),
-      ),
-      '#weight' => 30,
-    );
+
+    if (!content_type_extras_get_default('content_type_extras_disable_token_display')) {
+      $form['extras']['token_help'] = array(
+        '#title'       => t('Replacement patterns'),
+        '#type'        => 'fieldset',
+        '#collapsible' => TRUE,
+        '#collapsed'   => TRUE,
+        'help' => array(
+          '#theme'       => 'token_tree',
+          '#token_types' => array('node'),
+        ),
+        '#weight' => 30,
+      );
+    }
   }
   }
-  
+
   // Custom form submission handler
   // Custom form submission handler
   // See why this is done in the notes for the redirect function in
   // See why this is done in the notes for the redirect function in
   // content_type_extras.module
   // content_type_extras.module
@@ -260,9 +315,9 @@ function content_type_extras_node_type_form(&$form) {
 function content_type_extras_node_type_form_submit(&$form, &$form_state) {
 function content_type_extras_node_type_form_submit(&$form, &$form_state) {
   $values = $form_state['values'];
   $values = $form_state['values'];
   $user_permissions = $values['user_permissions'];
   $user_permissions = $values['user_permissions'];
-  
+
   $selected_perms = content_type_extras_get_selected_roles($user_permissions);
   $selected_perms = content_type_extras_get_selected_roles($user_permissions);
-  
+
   foreach ($user_permissions as $action => $group) {
   foreach ($user_permissions as $action => $group) {
     list($type, $trash) = explode('_', $action);
     list($type, $trash) = explode('_', $action);
     $set_perms = array();
     $set_perms = array();
@@ -271,13 +326,29 @@ function content_type_extras_node_type_form_submit(&$form, &$form_state) {
         'create ' . $values['type'] . ' content',
         'create ' . $values['type'] . ' content',
       );
       );
     }
     }
-    else {
-      $set_perms = array(
-        $type . ' own ' . $values['type'] . ' content',
-        $type . ' any ' . $values['type'] . ' content',
-      );
+    elseif ($type == 'edit') {
+      if($trash == 'own') {
+        $set_perms = array(
+          $type . ' own ' . $values['type'] . ' content',
+        );
+      } else {
+        $set_perms = array(
+          $type . ' any ' . $values['type'] . ' content',
+        );
+      }
     }
     }
-    
+    elseif ($type == 'delete') {
+      if($trash == 'own') {
+        $set_perms = array(
+          $type . ' own ' . $values['type'] . ' content',
+        );
+      } else {
+        $set_perms = array(
+          $type . ' any ' . $values['type'] . ' content',
+        );
+      }
+    }
+
     foreach ($group as $rid => $setting) {
     foreach ($group as $rid => $setting) {
       if ($setting) {
       if ($setting) {
         user_role_grant_permissions($rid, $set_perms);
         user_role_grant_permissions($rid, $set_perms);
@@ -287,7 +358,7 @@ function content_type_extras_node_type_form_submit(&$form, &$form_state) {
       }
       }
     }
     }
   }
   }
-  
+
   if (!empty($values['content_type_extras_remove_body'])) {
   if (!empty($values['content_type_extras_remove_body'])) {
     // I'm not sure of a better way to not have a body field, other than to
     // I'm not sure of a better way to not have a body field, other than to
     // delete it after it has been created by Core.
     // delete it after it has been created by Core.
@@ -296,7 +367,7 @@ function content_type_extras_node_type_form_submit(&$form, &$form_state) {
   }
   }
   // Remove variable that is automatically created, it's not needed
   // Remove variable that is automatically created, it's not needed
   variable_del('content_type_extras_remove_body_' . $values['type']);
   variable_del('content_type_extras_remove_body_' . $values['type']);
-  
+
   // We have to rename the pathauto variable. Drupal by default creates one, but
   // We have to rename the pathauto variable. Drupal by default creates one, but
   // it's not named properly for pathauto to recognize it.
   // it's not named properly for pathauto to recognize it.
   variable_set('pathauto_node_' . $values['type'] . '_pattern', variable_get('pathauto_node_' . $values['type']));
   variable_set('pathauto_node_' . $values['type'] . '_pattern', variable_get('pathauto_node_' . $values['type']));
@@ -314,10 +385,10 @@ function content_type_extras_get_selected_roles($permissions, $perm_type = NULL)
       }
       }
     }
     }
   }
   }
-  
+
   if (!empty($perm_type)) {
   if (!empty($perm_type)) {
     return $selected_perms[$perm_type];
     return $selected_perms[$perm_type];
   }
   }
-  
+
   return $selected_perms;
   return $selected_perms;
 }
 }

+ 44 - 9
sites/all/modules/contrib/admin/content_type_extras/js/content_type_extras.cancel_button.js

@@ -1,19 +1,54 @@
 (function ($) {
 (function ($) {
   Drupal.behaviors.content_type_extras_cancel_button = {
   Drupal.behaviors.content_type_extras_cancel_button = {
     attach: function(context, settings) {
     attach: function(context, settings) {
-      
-      if($('form.node-form #edit-cancel').hasClass('cte-processed') ||
-         $('form.node-form #edit-cancel--2').hasClass('cte-processed')) {
-           return;
+      var cancelButton = $('form.node-form [id^="edit-cancel"]');
+
+      if (cancelButton.hasClass('cte-processed')) {
+        return;
       }
       }
       else {
       else {
-        $('form.node-form #edit-cancel, form.node-form #edit-cancel--2').addClass('cte-processed');      
-        $('form.node-form #edit-cancel, form.node-form #edit-cancel--2').click(function() {
-         var answer = confirm(Drupal.t('Are you sure you want to cancel and lose all changes?'));
-          if (answer) {
+        var hide_warning = settings.content_type_extras.hide_warning;
+
+        cancelButton.addClass('cte-processed');
+        cancelButton.click(function() {
+          if (hide_warning == 0) {
+            var answer = confirm(Drupal.t('Are you sure you want to cancel and lose all changes?'));
+            if (answer) {
+              cteExecuteCancel();
+            }
+          }
+          else {
+            cteExecuteCancel();
+          }
+        });
+      }
+
+      function cteExecuteCancel() {
+        $.QueryString = (function(a) {
+          if (a == "") return {};
+          var b = {};
+          for (var i = 0; i < a.length; ++i) {
+            var p=a[i].split('=');
+            if (p.length != 2) continue;
+            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
+          }
+          return b;
+        })(window.location.search.substr(1).split('&'))
+
+        var baseUrl = document.location.origin;
+
+        if ($.QueryString["destination"]) {
+          var destination = $.QueryString["destination"];
+          $(location).attr('href', baseUrl + '/' + destination);
+        }
+        else {
+          if (settings.content_type_extras.cancel_location == 'static_path') {
+            window.location = settings.content_type_extras.location_path;
+          }
+          else {
             history.go(-1);
             history.go(-1);
           }
           }
-        });      
+        }
       }
       }
     }
     }
   }
   }

+ 3 - 3
sites/all/modules/contrib/admin/context/context.info

@@ -8,9 +8,9 @@ files[] = tests/context.test
 files[] = tests/context.conditions.test
 files[] = tests/context.conditions.test
 files[] = tests/context.reactions.test
 files[] = tests/context.reactions.test
 
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 core = "7.x"
 project = "context"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 
 

+ 1 - 1
sites/all/modules/contrib/admin/context/context.install

@@ -103,7 +103,7 @@ function context_update_7000() {
     }
     }
   }
   }
   if (empty($updated)) {
   if (empty($updated)) {
-    $ret = t('No contexts requiring migration detected');
+    $ret = t('No contexts requiring migration detected.');
   }
   }
   else {
   else {
     $ret = t('The following contexts had theme reaction data migrated: @names', array('@names' => join(', ', $updated)));
     $ret = t('The following contexts had theme reaction data migrated: @names', array('@names' => join(', ', $updated)));

+ 55 - 5
sites/all/modules/contrib/admin/context/context.module

@@ -192,11 +192,61 @@ function context_init() {
  * theme('menu_link') for the menu rendering to html.
  * theme('menu_link') for the menu rendering to html.
  */
  */
 function context_preprocess_menu_link(&$variables) {
 function context_preprocess_menu_link(&$variables) {
-  if($contexts = context_active_contexts()){
-    foreach($contexts as $context){
-      if((isset($context->reactions['menu']))){
-        if ($variables['element']['#href'] == $context->reactions['menu']) {
-          $variables['element']['#localized_options']['attributes']['class'][] = "active";
+  if ($contexts = context_active_contexts()) {
+    foreach ($contexts as $context) {
+      if (isset($context->reactions['menu'])) {
+        // In context module < v3.2 the url was a string. In version 3.3+ this is
+        // an array of urls. Implement interims BC layer.
+        //
+        // Examples:
+        // - OLD < v3.2 context reaction structure:
+        // array('menu' => 'taxonomy/term/1')
+        //
+        // - NEW 3.3+ context reaction structure:
+        // array(
+        //   'menu' => array(
+        //     0 => 'navigation:taxonomy/term/1'
+        //     1 => 'foo-menu:taxonomy/term/1'
+        //   )
+        // )
+        $reactions_menu = is_array($context->reactions['menu']) ? array_values($context->reactions['menu']) : array($context->reactions['menu']);
+
+        // Get everything after the first ':' character (if found) as the url to
+        // match against element '#href'.
+        $urls = array();
+        foreach ($reactions_menu as $url) {
+          if (strpos($url, ':') !== FALSE) {
+            // Get unique menu name 'navigation' from 'navigation:taxonomy/term/1'
+            $reaction_menu = explode(':', $url);
+            $path = $reaction_menu[1];
+            $urls[$path] = $reaction_menu[0];
+          }
+          else {
+            // BC layer for menu contexts that have not re-saved. This is for
+            // urls like 'taxonomy/term/1'. We need to add a fake menu key
+            // 'bc-context-menu-layer' or the BC link get's removed by
+            // array_intersect below.
+            //
+            // @TODO: Remove BC layer in 4.x
+            $urls[$url] = 'context-reaction-menu-bc-layer';
+          }
+        }
+
+        // Filter urls by the menu name of the current link. The link reaction
+        // can be configured per menu link in specific menus and the contect
+        // reaction should not applied to other menus with the same menu link.
+        $menu_name = $variables['element']['#original_link']['menu_name'];
+        $menu_paths = array_intersect($urls, array($menu_name, 'context-reaction-menu-bc-layer'));
+        $reaction_menu_paths = array_keys($menu_paths);
+
+        // - If menu href and context reaction menu url match, add the 'active'
+        //   css class to the link of this menu.
+        // - Do not add class twice on current page.
+        if (in_array($variables['element']['#href'], $reaction_menu_paths) && $variables['element']['#href'] != $_GET['q']) {
+          // Do not add the 'active' class twice in views tabs.
+          if (!in_array('active', $variables['element']['#localized_options']['attributes']['class'])) {
+            $variables['element']['#localized_options']['attributes']['class'][] = 'active';
+          }
         }
         }
       }
       }
     }
     }

+ 2 - 2
sites/all/modules/contrib/admin/context/context.plugins.inc

@@ -37,7 +37,7 @@ function _context_context_registry() {
       'plugin' => 'context_condition_path',
       'plugin' => 'context_condition_path',
     ),
     ),
     'query_string' => array(
     'query_string' => array(
-      'title' => t('Query String'),
+      'title' => t('Query string'),
       'description' => t('Set this context when any of the query strings above match the page query string. Put each query string on a separate line. You can use the "*" character as a wildcard and <code>~</code> to exclude one or more query strings.'),
       'description' => t('Set this context when any of the query strings above match the page query string. Put each query string on a separate line. You can use the "*" character as a wildcard and <code>~</code> to exclude one or more query strings.'),
       'plugin' => 'context_condition_query_string',
       'plugin' => 'context_condition_query_string',
     ),
     ),
@@ -119,7 +119,7 @@ function _context_context_registry() {
       'plugin' => 'context_reaction_template_suggestions',
       'plugin' => 'context_reaction_template_suggestions',
     ),
     ),
     'theme' => array(
     'theme' => array(
-      'title' => t('Theme Page'),
+      'title' => t('Theme page'),
       'description' => t('Control page theme variables using context.'),
       'description' => t('Control page theme variables using context.'),
       'plugin' => 'context_reaction_theme',
       'plugin' => 'context_reaction_theme',
     ),
     ),

+ 3 - 3
sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info

@@ -6,9 +6,9 @@ core = 7.x
 
 
 files[] = plugins/context_layouts_reaction_block.inc
 files[] = plugins/context_layouts_reaction_block.inc
 
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 core = "7.x"
 project = "context"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 
 

+ 3 - 3
sites/all/modules/contrib/admin/context/context_ui/context_ui.info

@@ -8,9 +8,9 @@ configure = admin/structure/context
 files[] = context.module
 files[] = context.module
 files[] = tests/context_ui.test
 files[] = tests/context_ui.test
 
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 core = "7.x"
 project = "context"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 
 

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui.js

@@ -12,11 +12,11 @@ function DrupalContextPlugins(form) {
     $('.context-plugin-list > li', this.form).each(function() {
     $('.context-plugin-list > li', this.form).each(function() {
       var plugin = $(this).attr('class').split('context-plugin-')[1].split(' ')[0];
       var plugin = $(this).attr('class').split('context-plugin-')[1].split(' ')[0];
       if ($(this).is('.disabled')) {
       if ($(this).is('.disabled')) {
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).show();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).show();
       }
       }
       else {
       else {
         state.push(plugin);
         state.push(plugin);
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).hide();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).hide();
       }
       }
     });
     });
     // Set the hidden plugin list state.
     // Set the hidden plugin list state.

+ 8 - 12
sites/all/modules/contrib/admin/context/context_ui/context_ui.module

@@ -81,7 +81,7 @@ function context_ui_permission() {
     'description' => 'Associate menus, views, blocks, etc. with different contexts to structure your site.'
     'description' => 'Associate menus, views, blocks, etc. with different contexts to structure your site.'
   );
   );
   $permissions['context ajax block access'] = array(
   $permissions['context ajax block access'] = array(
-    'title' => t('Access All Blocks'),
+    'title' => t('Access all blocks'),
     'description' => t('Allows users to access all rendered blocks via an AJAX callback. If you have some blocks that should not be rendered for some users but need those users to be able to use context UI, then implement hook_context_allow_ajax_block_access with the necessary logic.'),
     'description' => t('Allows users to access all rendered blocks via an AJAX callback. If you have some blocks that should not be rendered for some users but need those users to be able to use context UI, then implement hook_context_allow_ajax_block_access with the necessary logic.'),
   );
   );
   return $permissions;
   return $permissions;
@@ -143,7 +143,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
 
 
   $form['title'] = array(
   $form['title'] = array(
     '#prefix' => '<h2 class="context-editor-title">',
     '#prefix' => '<h2 class="context-editor-title">',
-    '#markup' => t('Select the Context/Layer to Edit'),
+    '#markup' => t('Select the context/layer to edit'),
     '#suffix' => '</h2>',
     '#suffix' => '</h2>',
     '#weight' => -2,
     '#weight' => -2,
   );
   );
@@ -151,10 +151,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
   //add some help text to the top of the form
   //add some help text to the top of the form
   $form['help'] = array (
   $form['help'] = array (
     '#prefix' => '<p class="context-help help">',
     '#prefix' => '<p class="context-help help">',
-    '#markup' => t('Select which context, or layer of blocks, to edit.
-                    Each context is configured to appear on different sets of pages so read the description carefully.
-                    When you are done editing click Done and save your changes.
-                    You may use the Stop Editing Layout link to close the editor.'),
+    '#markup' => t('Select which context, or layer of blocks, to edit. Each context is configured to appear on different sets of pages so read the description carefully. When you are done editing click Done and save your changes. You may use the Stop Editing Layout link to close the editor.'),
     '#suffix' => '</p>',
     '#suffix' => '</p>',
     '#weight' => -1,
     '#weight' => -1,
   );
   );
@@ -166,10 +163,9 @@ function context_ui_editor($form, &$form_state, $contexts) {
     $edit = l(t('Edit'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('edit'))));
     $edit = l(t('Edit'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('edit'))));
     $done = l(t('Done'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('done'))));
     $done = l(t('Done'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('done'))));
     $readable_name = ucwords(str_replace('_', ' ', $context->name));
     $readable_name = ucwords(str_replace('_', ' ', $context->name));
-    $description = empty($context->description) ? '' :
-                        "<br/><div class='label bottom'>".check_plain($context->description)."</div>";
+    $description = empty($context->description) ? '' : '<br/><div class="label bottom">' . check_plain($context->description) . '</div>';
     $items[] = array(
     $items[] = array(
-      'data' => "<div class='label top'>" . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
+      'data' => '<div class="label top">' . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
       'class' => array('context-editable clearfix'),
       'class' => array('context-editable clearfix'),
       'id' => "context-editable-trigger-{$context->name}",
       'id' => "context-editable-trigger-{$context->name}",
     );
     );
@@ -306,9 +302,9 @@ function context_ui_settings($form, &$form_state) {
   }
   }
   $form['context_ui_dialog_enabled'] = array(
   $form['context_ui_dialog_enabled'] = array(
     '#type' => 'checkbox',
     '#type' => 'checkbox',
-    '#title' => t('Use Context Editor Dialog'),
+    '#title' => t('Use context editor dialog'),
     '#default_value' => context_ui_dialog_is_enabled(),
     '#default_value' => context_ui_dialog_is_enabled(),
-    '#description' => t('When enabled all contextual links will have a Edit Layout link that will refresh the page with the context editor in a dialog box.'),
+    '#description' => t('When enabled all contextual links will have a <em>Edit layout</em> link that will refresh the page with the context editor in a dialog box.'),
   );
   );
   $form = system_settings_form($form);
   $form = system_settings_form($form);
   $form['#submit'][] = 'context_ui_settings_submit';
   $form['#submit'][] = 'context_ui_settings_submit';
@@ -374,7 +370,7 @@ function context_ui_menu_contextual_links_alter(&$links, $router_item, $root_pat
      !context_isset('context_ui', 'context_ui_editor_present') && user_access('administer contexts')) {
      !context_isset('context_ui', 'context_ui_editor_present') && user_access('administer contexts')) {
     $links['layout'] = array(
     $links['layout'] = array(
       'href' => 'context-ui/activate',
       'href' => 'context-ui/activate',
-      'title' => t('Configure Layout'),
+      'title' => t('Configure layout'),
       'localized_options' => array(
       'localized_options' => array(
         'query' =>  array('destination'=> $_GET['q']),
         'query' =>  array('destination'=> $_GET['q']),
         'options' => array('html' => FALSE, 'attributes' => array()),
         'options' => array('html' => FALSE, 'attributes' => array()),

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js

@@ -9,11 +9,11 @@
         selector.detach();
         selector.detach();
         $('#page').prepend(selector);
         $('#page').prepend(selector);
 
 
-        var labelOpen = Drupal.t('Select Context');
+        var labelOpen = Drupal.t('Select context');
         var labelClose = Drupal.t('Hide');
         var labelClose = Drupal.t('Hide');
 
 
         // Create a tab to show/hide our edit area
         // Create a tab to show/hide our edit area
-        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="Show Context Selector">'+labelClose+'</a>');
+        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="' + Drupal.t('Show context selector') + '">'+labelClose+'</a>');
         selector.append(tab);
         selector.append(tab);
 
 
         selector.toggled = false;
         selector.toggled = false;

+ 11 - 11
sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php

@@ -22,7 +22,7 @@ class context_export_ui extends ctools_export_ui {
   function list_render(&$form_state) {
   function list_render(&$form_state) {
     $table = array(
     $table = array(
       'header' => $this->list_table_header(),
       'header' => $this->list_table_header(),
-      'rows' => $this->rows, 
+      'rows' => $this->rows,
       'attributes' => array(
       'attributes' => array(
         'class' => array('context-admin'),
         'class' => array('context-admin'),
         'id' => 'ctools-export-ui-list-items',
         'id' => 'ctools-export-ui-list-items',
@@ -104,17 +104,17 @@ class context_export_ui extends ctools_export_ui {
  * @param $form_state
  * @param $form_state
  *   Form state array
  *   Form state array
  */
  */
-function context_ui_form(&$form, &$form_state) {  
+function context_ui_form(&$form, &$form_state) {
   $conditions = array_keys(context_conditions());
   $conditions = array_keys(context_conditions());
   sort($conditions);
   sort($conditions);
   $reactions = array_keys(context_reactions());
   $reactions = array_keys(context_reactions());
   sort($reactions);
   sort($reactions);
-    
+
   $context = $form_state['item'];
   $context = $form_state['item'];
   if (!empty($form_state['input'])) {
   if (!empty($form_state['input'])) {
     $context = _context_ui_rebuild_from_input($context, $form_state['input'], $conditions, $reactions);
     $context = _context_ui_rebuild_from_input($context, $form_state['input'], $conditions, $reactions);
   }
   }
-  
+
   $form['#base'] = 'context_ui_form';
   $form['#base'] = 'context_ui_form';
   $form['#theme'] = 'context_ui_form';
   $form['#theme'] = 'context_ui_form';
 
 
@@ -131,7 +131,7 @@ function context_ui_form(&$form, &$form_state) {
     '#required' => FALSE,
     '#required' => FALSE,
     '#maxlength' => 255,
     '#maxlength' => 255,
     '#default_value' => isset($context->tag) ? $context->tag : '',
     '#default_value' => isset($context->tag) ? $context->tag : '',
-    '#description' => t('Example: <code>theme</code>') .'<br/>'. t('A tag to group this context with others.'),
+    '#description' => t('Example: <code>theme</code>') . '<br/>' . t('A tag to group this context with others.'),
   );
   );
 
 
   $form['info']['description'] = array(
   $form['info']['description'] = array(
@@ -155,11 +155,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['conditions'] = array(
   $form['conditions'] = array(
     '#theme' => 'context_ui_plugins',
     '#theme' => 'context_ui_plugins',
     '#title' => t('Conditions'),
     '#title' => t('Conditions'),
-    '#description' => t('Trigger the activation of this context'),
+    '#description' => t('Trigger the activation of this context.'),
     '#tree' => TRUE,
     '#tree' => TRUE,
     'selector' => array(
     'selector' => array(
       '#type' => 'select',
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a condition') .'>'),
+      '#options' => array(0 => '<' . t('Add a condition') . '>'),
       '#default_value' => 0,
       '#default_value' => 0,
     ),
     ),
     'state' => array(
     'state' => array(
@@ -185,11 +185,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['reactions'] = array(
   $form['reactions'] = array(
     '#theme' => 'context_ui_plugins',
     '#theme' => 'context_ui_plugins',
     '#title' => t('Reactions'),
     '#title' => t('Reactions'),
-    '#description' => t('Actions to take when this context is active'),
+    '#description' => t('Actions to take when this context is active.'),
     '#tree' => TRUE,
     '#tree' => TRUE,
     'selector' => array(
     'selector' => array(
       '#type' => 'select',
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a reaction') .'>'),
+      '#options' => array(0 => '<' . t('Add a reaction') . '>'),
       '#default_value' => 0,
       '#default_value' => 0,
     ),
     ),
     'state' => array(
     'state' => array(
@@ -225,7 +225,7 @@ function context_ui_form(&$form, &$form_state) {
  *   A context object
  *   A context object
  */
  */
 function _context_ui_rebuild_from_input($context, $input, $conditions, $reactions) {
 function _context_ui_rebuild_from_input($context, $input, $conditions, $reactions) {
-  $condition_defaults = array();  
+  $condition_defaults = array();
   foreach ($conditions as $condition) {
   foreach ($conditions as $condition) {
     if ($plugin = context_get_plugin('condition', $condition)) {
     if ($plugin = context_get_plugin('condition', $condition)) {
       $condition_defaults[$condition] = array(
       $condition_defaults[$condition] = array(
@@ -235,7 +235,7 @@ function _context_ui_rebuild_from_input($context, $input, $conditions, $reaction
     }
     }
   }
   }
   $input['conditions']['plugins'] = array_merge($condition_defaults, $input['conditions']['plugins']);
   $input['conditions']['plugins'] = array_merge($condition_defaults, $input['conditions']['plugins']);
-  
+
   $reaction_defaults = array();
   $reaction_defaults = array();
   foreach ($reactions as $reaction) {
   foreach ($reactions as $reaction) {
     if ($plugin = context_get_plugin('reaction', $reaction)) {
     if ($plugin = context_get_plugin('reaction', $reaction)) {

+ 1 - 1
sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test

@@ -48,7 +48,7 @@ class ContextUiTestCase extends DrupalWebTestCase {
       'description' => $context->description,
       'description' => $context->description,
       'tag' => $context->tag,
       'tag' => $context->tag,
       'conditions[plugins][node][values][blog]' => 'blog',
       'conditions[plugins][node][values][blog]' => 'blog',
-      'reactions[plugins][menu]' => 'node/add/blog',
+      'reactions[plugins][menu][]' => 'navigation:node/add/blog',
     );
     );
     $this->drupalPost('admin/structure/context/add', $edit, 'Save');
     $this->drupalPost('admin/structure/context/add', $edit, 'Save');
     $this->assertText($context->name . ' has been created.', 'Context saved.');
     $this->assertText($context->name . ' has been created.', 'Context saved.');

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc

@@ -8,7 +8,7 @@ class context_condition_bookroot extends context_condition_node {
     if ($this->condition_used() && !empty($node->book['bid'])) {
     if ($this->condition_used() && !empty($node->book['bid'])) {
       $type = db_select('node')
       $type = db_select('node')
         ->fields('node', array('type'))
         ->fields('node', array('type'))
-        ->condition('nid', $node->book['nid'])
+        ->condition('nid', $node->book['bid'])
         ->execute()
         ->execute()
         ->fetchField();
         ->fetchField();
       $book = new stdClass();
       $book = new stdClass();

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc

@@ -14,7 +14,7 @@ class context_condition_menu extends context_condition {
       foreach ($menus as $key => $name) {
       foreach ($menus as $key => $name) {
         $id = explode(':', $key);
         $id = explode(':', $key);
         if ($id[1] == '0') {
         if ($id[1] == '0') {
-          $root_menus[$id[0]] = check_plain($name);
+          $root_menus[$id[0]] = $name;
         }
         }
         else {
         else {
           $link = menu_link_load($id[1]);
           $link = menu_link_load($id[1]);

+ 9 - 5
sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc

@@ -45,24 +45,28 @@ class context_condition_node_taxonomy extends context_condition_node {
     $check_fields = array();
     $check_fields = array();
     foreach ($instance_fields as $key => $field_info) {
     foreach ($instance_fields as $key => $field_info) {
       if ($fields[$key]['type'] == 'taxonomy_term_reference') {
       if ($fields[$key]['type'] == 'taxonomy_term_reference') {
-        $check_fields[] = $key;
+        $check_fields[$key] = 'tid';
+      }
+      else if ($fields[$key]['type'] == 'entityreference' &&
+               $fields[$key]['settings']['target_type'] == 'taxonomy_term') {
+        $check_fields[$key] = 'target_id';
       }
       }
     }
     }
 
 
     if ($this->condition_used() && !empty($check_fields)) {
     if ($this->condition_used() && !empty($check_fields)) {
-      foreach ($check_fields as $field) {
+      foreach ($check_fields as $field => $term_id_key) {
         if ($terms = field_get_items('node', $node, $field)) {
         if ($terms = field_get_items('node', $node, $field)) {
           foreach ($terms as $term) {
           foreach ($terms as $term) {
-            foreach ($this->get_contexts($term['tid']) as $context) {
+            foreach ($this->get_contexts($term[$term_id_key]) as $context) {
               // Check the node form option.
               // Check the node form option.
               if ($op === 'form') {
               if ($op === 'form') {
                 $options = $this->fetch_from_context($context, 'options');
                 $options = $this->fetch_from_context($context, 'options');
                 if (!empty($options['node_form'])) {
                 if (!empty($options['node_form'])) {
-                  $this->condition_met($context, $term['tid']);
+                  $this->condition_met($context, $term[$term_id_key]);
                 }
                 }
               }
               }
               else {
               else {
-                $this->condition_met($context, $term['tid']);
+                $this->condition_met($context, $term[$term_id_key]);
               }
               }
             }
             }
           }
           }

+ 4 - 5
sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js

@@ -69,7 +69,7 @@ DrupalContextBlockForm = function(blockForm) {
     // Hide enabled blocks from selector that are used
     // Hide enabled blocks from selector that are used
     $('table.context-blockform-region tr').each(function() {
     $('table.context-blockform-region tr').each(function() {
       var bid = $(this).attr('id');
       var bid = $(this).attr('id');
-      $('div.context-blockform-selector input[value='+bid+']').parents('div.form-item').eq(0).hide();
+      $('div.context-blockform-selector input[value="'+bid+'"]').parents('div.form-item').eq(0).hide();
     });
     });
     // Show blocks in selector that are unused
     // Show blocks in selector that are unused
     $('div.context-blockform-selector input').each(function() {
     $('div.context-blockform-selector input').each(function() {
@@ -159,7 +159,7 @@ DrupalContextBlockForm = function(blockForm) {
           $(this).removeAttr('checked');
           $(this).removeAttr('checked');
         });
         });
         if (weight_warn) {
         if (weight_warn) {
-          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving'));
+          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving.'));
         }
         }
       }
       }
       return false;
       return false;
@@ -426,9 +426,8 @@ DrupalContextBlockEditor.prototype = {
 
 
     $('.editing-context-label').remove();
     $('.editing-context-label').remove();
     var label = $('#context-editable-trigger-'+context+' .label').text();
     var label = $('#context-editable-trigger-'+context+' .label').text();
-    label = Drupal.t('Now Editing: ') + label;
-    editor.parent().parent()
-      .prepend('<div class="editing-context-label">'+ label + '</div>');
+    label = Drupal.t('Now editing: @label', {'@label': label});
+    editor.parent().parent().prepend('<div class="editing-context-label">' + label + '</div>');
 
 
     // First pass, enable sortables on all regions.
     // First pass, enable sortables on all regions.
     $(this.regions).each(function() {
     $(this.regions).each(function() {

+ 1 - 0
sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc

@@ -8,6 +8,7 @@ class context_reaction_breadcrumb extends context_reaction_menu {
    * Overrides set_active_trail_from_link to set the breadcrumb instead of the menu path.
    * Overrides set_active_trail_from_link to set the breadcrumb instead of the menu path.
    */
    */
   function set_active_trail_from_link($item) {
   function set_active_trail_from_link($item) {
+    $breadcrumb = array(l(t('Home'), '<front>'));
     $result = db_select('menu_links')
     $result = db_select('menu_links')
       ->fields('menu_links', array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'))
       ->fields('menu_links', array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'))
       ->condition('hidden', 0)
       ->condition('hidden', 0)

+ 3 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc

@@ -22,19 +22,21 @@ class context_reaction_menu extends context_reaction {
           $link = menu_link_load($mlid);
           $link = menu_link_load($mlid);
           $identifier = $link['link_path'];
           $identifier = $link['link_path'];
           $root_menu = $menu_names[$menu_name];
           $root_menu = $menu_names[$menu_name];
-          while (isset($options[$root_menu][$identifier])) {
+          while (isset($options[$root_menu][$menu_name . ':' . $identifier])) {
             $identifier .= "'";
             $identifier .= "'";
           }
           }
           $options[$root_menu][$menu_name . ':' . $identifier] = $title;
           $options[$root_menu][$menu_name . ':' . $identifier] = $title;
         }
         }
       }
       }
     }
     }
+    $menu_count = count($options, COUNT_RECURSIVE);
     return array(
     return array(
       '#title' => $this->title,
       '#title' => $this->title,
       '#description' => $this->description,
       '#description' => $this->description,
       '#options' => $options,
       '#options' => $options,
       '#type' => 'select',
       '#type' => 'select',
       '#multiple' => TRUE,
       '#multiple' => TRUE,
+      '#size' => $menu_count > 20 ? 20 : $menu_count,
       '#default_value' => $this->fetch_from_context($context),
       '#default_value' => $this->fetch_from_context($context),
     );
     );
   }
   }

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc

@@ -14,7 +14,7 @@ class context_reaction_template_suggestions extends context_reaction {
     return array(
     return array(
       '#title' => t('Template suggestions'),
       '#title' => t('Template suggestions'),
       '#type' => 'textarea',
       '#type' => 'textarea',
-      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens).  For more information, please visit ') . l(t('Drupal 7 Template (Theme Hook) Suggestions'), 'http://drupal.org/node/1089656', array(array('target' => '_blank'), 'html' => TRUE,)) . '.',
+      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens). For more information, please visit <a href="@template-suggestions">Drupal 7 Template (Theme Hook) Suggestions</a>.', array('@template-suggestions' => 'http://drupal.org/node/1089656')),
       '#default_value' => is_string($default_value) ? $default_value : '',
       '#default_value' => is_string($default_value) ? $default_value : '',
     );
     );
   }
   }

+ 0 - 22
sites/all/modules/contrib/admin/context/tests/context.conditions.test

@@ -36,13 +36,6 @@ class ContextConditionUserTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
   }
 
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
   function test() {
     // User 1 triggers the context.
     // User 1 triggers the context.
     $this->drupalLogin($this->user1);
     $this->drupalLogin($this->user1);
@@ -82,14 +75,6 @@ class ContextConditionUserPageTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
   }
 
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    $edit = array();
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
   function test() {
     // Viewing any user profile triggers context.
     // Viewing any user profile triggers context.
     $this->drupalLogin($this->user1);
     $this->drupalLogin($this->user1);
@@ -157,13 +142,6 @@ class ContextConditionNodeTaxonomyTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
   }
 
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    taxonomy_term_delete($this->terms['apples']->tid);
-    taxonomy_term_delete($this->terms['oranges']->tid);
-  }
-
   function test() {
   function test() {
     // Apples does trigger the context.
     // Apples does trigger the context.
     $edit = array(
     $edit = array(

+ 3 - 4
sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc

@@ -52,7 +52,7 @@ function theme_context_block_regions_form($vars) {
     $output .= "<div class='label context-blockform-regionlabel-{$region}'>";
     $output .= "<div class='label context-blockform-regionlabel-{$region}'>";
     $output .= l(t('+') . ' ' . t('Add'), $_GET['q'], array('fragment' => $region, 'attributes' => array('class' => array('add-block'))));
     $output .= l(t('+') . ' ' . t('Add'), $_GET['q'], array('fragment' => $region, 'attributes' => array('class' => array('add-block'))));
     $output .= $form[$region]['#title'];
     $output .= $form[$region]['#title'];
-    $output .= "</div>";
+    $output .= '</div>';
     $output .= theme('table', array('rows' => $rows, 'attributes' => $attr));
     $output .= theme('table', array('rows' => $rows, 'attributes' => $attr));
   }
   }
   return $output;
   return $output;
@@ -100,14 +100,13 @@ function template_preprocess_context_block_browser(&$vars) {
   //add help text to tell people how to use the block browser
   //add help text to tell people how to use the block browser
   $help_text = array(
   $help_text = array(
     '#prefix' => '<div class="context_ui-help-text">',
     '#prefix' => '<div class="context_ui-help-text">',
-    '#markup' => t('To add a block to the current region, simply click on the block.  You may use the category filter to filter by
-      block type or the search filter to find the block that you wish to add.'),
+    '#markup' => t('To add a block to the current region, simply click on the block. You may use the category filter to filter by block type or the search filter to find the block that you wish to add.'),
     '#suffix' => '</div>',
     '#suffix' => '</div>',
   );
   );
 
 
   $filter_label = array(
   $filter_label = array(
     '#prefix' => '<div class="filter-label">',
     '#prefix' => '<div class="filter-label">',
-    '#markup' => t('Search Filter'),
+    '#markup' => t('Search filter'),
     '#suffix' => '</div>',
     '#suffix' => '</div>',
   );
   );
 
 

+ 100 - 33
sites/all/modules/contrib/admin/features/features.admin.inc

@@ -88,6 +88,12 @@ function features_settings_form($form, $form_state) {
     '#default_value' => variable_get('features_rebuild_on_flush', TRUE),
     '#default_value' => variable_get('features_rebuild_on_flush', TRUE),
     '#description' => t('If you have a large site with many features, you may experience lag on full cache clear. If disabled, features will rebuild only when viewing the features list or saving the modules list.'),
     '#description' => t('If you have a large site with many features, you may experience lag on full cache clear. If disabled, features will rebuild only when viewing the features list or saving the modules list.'),
   );
   );
+  $form['general']['features_rebuild_modules_page'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Rebuild features on accessing modules list page'),
+    '#default_value' => variable_get('features_rebuild_modules_page', FALSE),
+    '#description' => t('If you have a large site with many features, you may experience lag on accessing the modules administration page. If disabled, features will not rebuild when viewing the modules list.'),
+  );
 
 
   return system_settings_form($form);
   return system_settings_form($form);
 }
 }
@@ -109,7 +115,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
 
 
   $feature_name = !empty($feature->name) ? $feature->name : '';
   $feature_name = !empty($feature->name) ? $feature->name : '';
   $form = array(
   $form = array(
-    '#attributes' => array('class' => array('features-export-form')),
+    '#attributes' => array('class' => array('features-export-form', 'clearfix')),
     '#feature' => isset($feature) ? $feature : NULL,
     '#feature' => isset($feature) ? $feature : NULL,
   );
   );
   $form['info'] = array(
   $form['info'] = array(
@@ -203,7 +209,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
     $form['advanced']['generate'] = array(
     $form['advanced']['generate'] = array(
       '#type' => 'submit',
       '#type' => 'submit',
       '#value' => t('Generate feature'),
       '#value' => t('Generate feature'),
-      '#submit' => array('features_export_build_form_submit'),
+      '#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
     );
     );
   }
   }
   // build the Component Listing panel on the right
   // build the Component Listing panel on the right
@@ -239,7 +245,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
     '#type' => 'submit',
     '#type' => 'submit',
     '#value' => t('Download feature'),
     '#value' => t('Download feature'),
     '#weight' => 10,
     '#weight' => 10,
-    '#submit' => array('features_export_build_form_submit'),
+    '#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
   );
   );
 
 
   $form['#attached']['library'][] = array('system', 'ui.dialog');
   $form['#attached']['library'][] = array('system', 'ui.dialog');
@@ -597,6 +603,7 @@ function _features_export_build($feature, &$form_state) {
       $component_export['selected'][$section] = array();
       $component_export['selected'][$section] = array();
     }
     }
     $options = features_invoke($component, 'features_export_options');
     $options = features_invoke($component, 'features_export_options');
+    drupal_alter('features_export_options', $options, $component);
     if (!empty($options)) {
     if (!empty($options)) {
       $exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : array();
       $exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : array();
       $new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : array();
       $new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : array();
@@ -755,7 +762,7 @@ function features_export_form_rebuild($form, &$form_state) {
 
 
 function features_export_components_json($feature_name) {
 function features_export_components_json($feature_name) {
   module_load_include('inc', 'features', 'features.export');
   module_load_include('inc', 'features', 'features.export');
-  $export = array();
+  $export = array('features' => array());
   if (!empty($_POST['items'])) {
   if (!empty($_POST['items'])) {
     $excluded = (!empty($_POST['excluded'])) ? $_POST['excluded'] : array();
     $excluded = (!empty($_POST['excluded'])) ? $_POST['excluded'] : array();
     $stub = array();
     $stub = array();
@@ -843,7 +850,7 @@ function _features_export_generate($export, $form_state, $feature = NULL) {
   }
   }
   // If either update status-related keys are provided, add a project key
   // If either update status-related keys are provided, add a project key
   // corresponding to the module name.
   // corresponding to the module name.
-  if (!empty($form_state['values']['version']) || !empty($form_state['values']['project_status_url'])) {
+  if (!empty($form_state['values']['version']) && !empty($form_state['values']['project_status_url'])) {
     $export['project'] = $form_state['values']['module_name'];
     $export['project'] = $form_state['values']['module_name'];
   }
   }
   if (!empty($form_state['values']['version'])) {
   if (!empty($form_state['values']['version'])) {
@@ -900,6 +907,35 @@ function features_export_build_form_submit($form, &$form_state) {
 
 
     $tar = array();
     $tar = array();
     $filenames = array();
     $filenames = array();
+    // Copy any files if _files key is there.
+    if (!empty($files['_files'])) {
+      foreach ($files['_files'] as $file_name => $file_info) {
+        if ($generate) {
+          // See if files are in a sub directory.
+          if (strpos($file_name, '/')) {
+            $file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
+            if (!is_dir($file_directory)) {
+              mkdir($file_directory);
+            }
+          }
+          if (!empty($file_info['file_path'])) {
+            file_unmanaged_copy($file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
+          }
+          elseif ($file_info['file_content']) {
+            file_put_contents("{$directory}/{$file_name}", $file_info['file_content']);
+          }
+        }
+        else {
+          if (!empty($file_info['file_path'])) {
+            print features_tar_create("{$module_name}/{$file_name}", file_get_contents($file_info['file_path']));
+          }
+          elseif ($file_info['file_content']) {
+            features_tar_create("{$directory}/{$file_name}", $file_info['file_content']);
+          }
+        }
+      }
+      unset($files['_files']);
+    }
     foreach ($files as $extension => $file_contents) {
     foreach ($files as $extension => $file_contents) {
       if (!in_array($extension, array('module', 'info'))) {
       if (!in_array($extension, array('module', 'info'))) {
         $extension .= '.inc';
         $extension .= '.inc';
@@ -973,28 +1009,14 @@ function features_filter_hidden($module) {
  * Form constructor for the features configuration form.
  * Form constructor for the features configuration form.
  */
  */
 function features_admin_form($form, $form_state) {
 function features_admin_form($form, $form_state) {
-  // Load export functions to use in comparison.
-  module_load_include('inc', 'features', 'features.export');
-
-  // Clear & rebuild key caches
-  features_get_info(NULL, NULL, TRUE);
-  features_rebuild();
-
+  $features = _features_get_features_list();
   $modules = array_filter(features_get_modules(), 'features_filter_hidden');
   $modules = array_filter(features_get_modules(), 'features_filter_hidden');
-  $features = array_filter(features_get_features(), 'features_filter_hidden');
   $conflicts = features_get_conflicts();
   $conflicts = features_get_conflicts();
 
 
-  foreach ($modules as $key => $module) {
-    if ($module->status && !empty($module->info['dependencies'])) {
-      foreach ($module->info['dependencies'] as $dependent) {
-        if (isset($features[$dependent])) {
-          $features[$dependent]->dependents[$key] = $module->info['name'];
-        }
-      }
-    }
-  }
+  // Load export functions to use in comparison.
+  module_load_include('inc', 'features', 'features.export');
 
 
-  if ( empty($features) ) {
+  if (empty($features) ) {
     $form['no_features'] = array(
     $form['no_features'] = array(
       '#markup' => t('No Features were found. Please use the !create_link link to create
       '#markup' => t('No Features were found. Please use the !create_link link to create
       a new Feature module, or upload an existing Feature to your modules directory.',
       a new Feature module, or upload an existing Feature to your modules directory.',
@@ -1073,6 +1095,7 @@ function features_admin_form($form, $form_state) {
     }
     }
 
 
     $href = "admin/structure/features/{$name}";
     $href = "admin/structure/features/{$name}";
+    $href_overridden = module_exists('diff') ? $href . '/diff' : $href;
     $module_name = (user_access('administer features')) ? l($module->info['name'], $href) : $module->info['name'];
     $module_name = (user_access('administer features')) ? l($module->info['name'], $href) : $module->info['name'];
     $form[$package]['status'][$name] = array(
     $form[$package]['status'][$name] = array(
       '#type' => 'checkbox',
       '#type' => 'checkbox',
@@ -1102,7 +1125,7 @@ function features_admin_form($form, $form_state) {
         $state .= l(t('Check'), "admin/structure/features/{$name}/status", array('attributes' => array('class' => array('admin-check'))));
         $state .= l(t('Check'), "admin/structure/features/{$name}/status", array('attributes' => array('class' => array('admin-check'))));
         $state .= theme('features_storage_link', array('storage' => FEATURES_REBUILDING, 'path' => $href));
         $state .= theme('features_storage_link', array('storage' => FEATURES_REBUILDING, 'path' => $href));
         $state .= theme('features_storage_link', array('storage' => FEATURES_NEEDS_REVIEW, 'path' =>  $href));
         $state .= theme('features_storage_link', array('storage' => FEATURES_NEEDS_REVIEW, 'path' =>  $href));
-        $state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' =>  $href));
+        $state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' =>  $href_overridden));
         $state .= theme('features_storage_link', array('storage' => FEATURES_DEFAULT, 'path' =>  $href));
         $state .= theme('features_storage_link', array('storage' => FEATURES_DEFAULT, 'path' =>  $href));
       }
       }
       elseif (!empty($conflicts[$name])) {
       elseif (!empty($conflicts[$name])) {
@@ -1126,7 +1149,7 @@ function features_admin_form($form, $form_state) {
   // As of 7.0 beta 2 it matters where the "vertical_tabs" element lives on the
   // As of 7.0 beta 2 it matters where the "vertical_tabs" element lives on the
   // the array. We add it late, but at the beginning of the array because that
   // the array. We add it late, but at the beginning of the array because that
   // keeps us away from trouble.
   // keeps us away from trouble.
-  $form = array('packages' => array('#type' => 'vertical_tabs')) + $form;
+  $form = array_merge(array('packages' => array('#type' => 'vertical_tabs')), $form);
 
 
   $form['buttons'] = array(
   $form['buttons'] = array(
     '#theme' => 'features_form_buttons',
     '#theme' => 'features_form_buttons',
@@ -1311,7 +1334,7 @@ function features_form_submit(&$form, &$form_state) {
   // page callback rather than as part of the submit handler as some modules
   // page callback rather than as part of the submit handler as some modules
   // have includes/other directives of importance in hooks that have already
   // have includes/other directives of importance in hooks that have already
   // been called in this page load.
   // been called in this page load.
-  $form_state['redirect'] = 'admin/structure/features/cleanup/clear';
+  $form_state['redirect'] = array('admin/structure/features/cleanup', array('query' => array('token' => drupal_get_token())));
 
 
   $features = $form['#features'];
   $features = $form['#features'];
   if (!empty($features)) {
   if (!empty($features)) {
@@ -1328,21 +1351,26 @@ function features_form_submit(&$form, &$form_state) {
 }
 }
 
 
 /**
 /**
- * Form for clearing cache after enabling a feature.
+ * Submit handler for the 'manage features' form rebuild button.
  */
  */
-function features_cleanup_form($form, $form_state, $cache_clear = FALSE) {
-  // Clear caches if we're getting a post-submit redirect that requests it.
-  if ($cache_clear) {
-    drupal_flush_all_caches();
+function features_form_rebuild() {
+  cache_clear_all('features:features_list', 'cache');
+}
 
 
+/**
+ * Callback for clearing cache after enabling a feature.
+ */
+function features_cleanup() {
+  if (!empty($_GET['token']) && drupal_valid_token($_GET['token'])) {
+    drupal_flush_all_caches();
     // The following functions need to be run because drupal_flush_all_caches()
     // The following functions need to be run because drupal_flush_all_caches()
     // runs rebuilds in the wrong order. The node type cache is rebuilt *after*
     // runs rebuilds in the wrong order. The node type cache is rebuilt *after*
     // the menu is rebuilt, meaning that the menu tree is stale in certain
     // the menu is rebuilt, meaning that the menu tree is stale in certain
     // circumstances after drupal_flush_all_caches(). We rebuild again.
     // circumstances after drupal_flush_all_caches(). We rebuild again.
     menu_rebuild();
     menu_rebuild();
-  }
-
     drupal_goto('admin/structure/features');
     drupal_goto('admin/structure/features');
+  }
+  return MENU_NOT_FOUND;
 }
 }
 
 
 /**
 /**
@@ -1587,3 +1615,42 @@ function _features_get_used($module_name = NULL) {
   $features_ignore_conflicts = $old_value;
   $features_ignore_conflicts = $old_value;
   return $conflicts;
   return $conflicts;
 }
 }
+
+/**
+ * Retrieves the array of features as expected on the Manage Features form.
+ * Uses caching for performance reasons if caching is enabled.
+ *
+ * @internal - This function might return cached result with outdated data,
+ * use with caution.
+ */
+function _features_get_features_list() {
+  $features = array();
+
+  $cache = cache_get('features:features_list');
+  if ($cache) {
+    $features = $cache->data;  
+  }
+  
+  if (empty($features)) {
+    // Clear & rebuild key caches
+    features_get_info(NULL, NULL, TRUE);
+    features_rebuild();
+
+    $modules = array_filter(features_get_modules(), 'features_filter_hidden');
+    $features = array_filter(features_get_features(), 'features_filter_hidden');
+
+    foreach ($modules as $key => $module) {
+      if ($module->status && !empty($module->info['dependencies'])) {
+        foreach ($module->info['dependencies'] as $dependent) {
+          if (isset($features[$dependent])) {
+            $features[$dependent]->dependents[$key] = $module->info['name'];
+          }
+        }
+      }
+    }
+
+    cache_set('features:features_list', $features);
+  }
+
+  return $features;
+}

+ 24 - 3
sites/all/modules/contrib/admin/features/features.api.php

@@ -42,7 +42,7 @@
  *   are declared "dynamically" or are part of a family of components.
  *   are declared "dynamically" or are part of a family of components.
  *
  *
  *   'alter_type': What type of alter hook this hook uses. 'normal' is called
  *   'alter_type': What type of alter hook this hook uses. 'normal' is called
- *   after the main hook is called. 'inline' is embeded within the default hook
+ *   after the main hook is called. 'inline' is embedded within the default hook
  *   and may not be implemented by some default hooks.
  *   and may not be implemented by some default hooks.
  *   'none' is no alter hook exists. Defaults to 'normal'
  *   'none' is no alter hook exists. Defaults to 'normal'
  *
  *
@@ -157,7 +157,8 @@ function hook_features_export_options() {
  *   of the module, e.g. the key for `hook_example` should simply be `example`
  *   of the module, e.g. the key for `hook_example` should simply be `example`
  *   The values in the array can also be in the form of an associative array
  *   The values in the array can also be in the form of an associative array
  *   with the required key of 'code' and optional key of 'args', if 'args' need
  *   with the required key of 'code' and optional key of 'args', if 'args' need
- *   to be added to the hook.
+ *   to be added to the hook. Alternate it can be an associative array in the
+ *   same style as hook_features_export_files() to add additional files.
  */
  */
 function hook_features_export_render($module_name, $data, $export = NULL) {
 function hook_features_export_render($module_name, $data, $export = NULL) {
   $code = array();
   $code = array();
@@ -309,11 +310,31 @@ function hook_features_pipe_COMPONENT_alter(&$pipe, $data, $export) {
  * The module being exported contained in $export['module_name'].
  * The module being exported contained in $export['module_name'].
  */
  */
 function hook_features_pipe_alter(&$pipe, $data, $export) {
 function hook_features_pipe_alter(&$pipe, $data, $export) {
-  if ($export['component'] == 'node' && in_array($data, 'my-node-type')) {
+  if ($export['component'] == 'node' && in_array('my-node-type', $data)) {
     $pipe['dependencies'][] = 'mymodule';
     $pipe['dependencies'][] = 'mymodule';
   }
   }
 }
 }
 
 
+
+/**
+ * Add extra files to the exported file.
+ *
+ * @return array
+ *   An array of files, keyed by file name that will appear in feature and
+ *   with either file_path key to indicate where to copy the file from or
+ *   file_content key to indicate the contents of the file.
+ */
+function hook_features_export_files($module_name, $export) {
+  return array('css/main.css' => array('file_content' => 'body {background-color:blue;}'));
+}
+
+/**
+ * Alter the extra files added to the export.
+ */
+function hook_features_export_files_alter(&$files, $module_name, $export) {
+  $files['css/main.css']['file_content'] = 'body {background-color:black;}';
+}
+
 /**
 /**
  * @defgroup features_component_alter_hooks Feature's component alter hooks
  * @defgroup features_component_alter_hooks Feature's component alter hooks
  * @{
  * @{

+ 150 - 21
sites/all/modules/contrib/admin/features/features.drush.inc

@@ -32,6 +32,12 @@ function features_drush_command() {
     ),
     ),
     'drupal dependencies' => array('features'),
     'drupal dependencies' => array('features'),
     'aliases' => array('fl', 'features'),
     'aliases' => array('fl', 'features'),
+    'outputformat' => array(
+      'default' => 'table',
+      'pipe-format' => 'list',
+      'field-labels' => array('name' => 'Name', 'feature' => 'Feature', 'status' => 'Status', 'version' => 'Version', 'state' => 'State'),
+      'output-data-type' => 'format-table',
+    ),
   );
   );
   $items['features-export'] = array(
   $items['features-export'] = array(
     'description' => "Export a feature from your site into a module.",
     'description' => "Export a feature from your site into a module.",
@@ -43,6 +49,7 @@ function features_drush_command() {
       'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to '" . $path . "'.",
       'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to '" . $path . "'.",
       'version-set' => "Specify a version number for the feature.",
       'version-set' => "Specify a version number for the feature.",
       'version-increment' => "Increment the feature's version number.",
       'version-increment' => "Increment the feature's version number.",
+      'ignore-conflicts' => "Ignore conflicts and export all components.",
     ),
     ),
     'drupal dependencies' => array('features'),
     'drupal dependencies' => array('features'),
     'aliases' => array('fe'),
     'aliases' => array('fe'),
@@ -72,6 +79,9 @@ function features_drush_command() {
       'not-exported' => array(
       'not-exported' => array(
         'description' => 'Show only components that have not been exported.',
         'description' => 'Show only components that have not been exported.',
       ),
       ),
+      'info-style' => array(
+        'description' => 'Export components in format suitable for using in an info file.',
+      ),
     ),
     ),
     'aliases' => array('fc'),
     'aliases' => array('fc'),
   );
   );
@@ -134,6 +144,18 @@ function features_drush_command() {
     'aliases' => array('fd'),
     'aliases' => array('fd'),
   );
   );
 
 
+  $items['features-diff-all'] = array(
+    'description' => "Show the code difference for all enabled features not in their default state.",
+    'arguments' => array(
+      'feature_exclude' => 'A space-delimited list of features to exclude from being reverted.',
+    ),
+    'options' => array(
+      'force' => "Bypass the confirmations. This is useful if you want to output all of the diffs to a log file.",
+    ),
+    'drupal dependencies' => array('features', 'diff'),
+    'aliases' => array('fda'),
+  );
+
   return $items;
   return $items;
 }
 }
 
 
@@ -191,12 +213,12 @@ function drush_features_list() {
   }
   }
 
 
   module_load_include('inc', 'features', 'features.export');
   module_load_include('inc', 'features', 'features.export');
-  $rows = array(array(dt('Name'), dt('Feature'), dt('Status'), dt('Version'), dt('State')));
 
 
   // Sort the Features list before compiling the output.
   // Sort the Features list before compiling the output.
   $features = features_get_features(NULL, TRUE);
   $features = features_get_features(NULL, TRUE);
   ksort($features);
   ksort($features);
 
 
+  $rows = array();
   foreach ($features as $k => $m) {
   foreach ($features as $k => $m) {
     switch (features_get_storage($m->name)) {
     switch (features_get_storage($m->name)) {
       case FEATURES_DEFAULT:
       case FEATURES_DEFAULT:
@@ -214,16 +236,19 @@ function drush_features_list() {
       ($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
       ($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
       ($m->status == 1 && ($status == 'all' || $status == 'enabled'))
       ($m->status == 1 && ($status == 'all' || $status == 'enabled'))
     ) {
     ) {
-      $rows[] = array(
-        $m->info['name'],
-        $m->name,
-        $m->status ? dt('Enabled') : dt('Disabled'),
-        $m->info['version'],
-        $storage
+      $rows[$k] = array(
+        'name' => $m->info['name'],
+        'feature' => $m->name,
+        'status' => $m->status ? dt('Enabled') : dt('Disabled'),
+        'version' => $m->info['version'],
+        'state' => $storage
       );
       );
     }
     }
   }
   }
-  drush_print_table($rows, TRUE);
+  if (version_compare(DRUSH_VERSION, '6.0', '<')) {
+    drush_print_table($rows, TRUE);
+  }
+  return $rows;
 }
 }
 
 
 /**
 /**
@@ -253,10 +278,13 @@ function drush_features_components() {
   elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
   elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
     $options['exported'] = FALSE;
     $options['exported'] = FALSE;
   }
   }
+  if (drush_get_option(array('info-style', 'is'), NULL)) {
+    $options['info style'] = TRUE;
+  }
 
 
   $filtered_components = _drush_features_component_filter($components, $args, $options);
   $filtered_components = _drush_features_component_filter($components, $args, $options);
   if ($filtered_components){
   if ($filtered_components){
-    _drush_features_component_print($filtered_components);
+    _drush_features_component_print($filtered_components, $options);
   }
   }
 }
 }
 
 
@@ -290,7 +318,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
   // First filter on exported state.
   // First filter on exported state.
   foreach ($all_components as $source => $components) {
   foreach ($all_components as $source => $components) {
     foreach ($components as $name => $title) {
     foreach ($components as $name => $title) {
-      $exported = sizeof($components_map[$source][$name]) > 0;
+      $exported = !empty($components_map[$source][$name]);
       if ($exported) {
       if ($exported) {
         if ($options['exported']) {
         if ($options['exported']) {
           $pool[$source][$name] = $title;
           $pool[$source][$name] = $title;
@@ -318,7 +346,13 @@ function _drush_features_component_filter($all_components, $patterns = array(),
     // Rewrite * to %. Let users use both as wildcard.
     // Rewrite * to %. Let users use both as wildcard.
     $pattern = strtr($pattern, array('*' => '%'));
     $pattern = strtr($pattern, array('*' => '%'));
     $sources = array();
     $sources = array();
-    list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
+    if (strpos($pattern, ':') !== FALSE) {
+      list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
+    }
+    else {
+      $source_pattern = $pattern;
+      $component_pattern = '';
+    }
     // If source is empty, use a pattern.
     // If source is empty, use a pattern.
     if ($source_pattern == '') {
     if ($source_pattern == '') {
       $source_pattern = '%';
       $source_pattern = '%';
@@ -383,7 +417,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
             return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
             return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
           }
           }
         }
         }
-        if (!is_array($selected[$source])) {
+        if (empty($selected[$source])) {
           $selected[$source] = array();
           $selected[$source] = array();
         }
         }
         $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
         $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
@@ -404,7 +438,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
   if ($options['provided by'] && $options['exported'] ) {
   if ($options['provided by'] && $options['exported'] ) {
     foreach ($selected as $source => $components) {
     foreach ($selected as $source => $components) {
       foreach ($components as $name => $title) {
       foreach ($components as $name => $title) {
-        $exported = sizeof($components_map[$source][$name]) > 0;
+        $exported = !empty($components_map[$source][$name]);
         if ($exported) {
         if ($exported) {
           $provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
           $provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
         }
         }
@@ -421,11 +455,17 @@ function _drush_features_component_filter($all_components, $patterns = array(),
 /**
 /**
  * Prints a list of filtered components.
  * Prints a list of filtered components.
  */
  */
-function _drush_features_component_print($filtered_components) {
+function _drush_features_component_print($filtered_components, $options = array()) {
   $rows = array(array(dt('Available sources')));
   $rows = array(array(dt('Available sources')));
   foreach ($filtered_components['components'] as $source => $components) {
   foreach ($filtered_components['components'] as $source => $components) {
     foreach ($components as $name => $value) {
     foreach ($components as $name => $value) {
-      $row = array($source .':'. $name);
+      if (!empty($options['info style'])) {
+        // Output as .info file style.
+        $row = array('features[' . $source . '][] = "' . $name . '"');
+      }
+      else {
+        $row = array($source .':'. $name);
+      }
       if (isset($filtered_components['sources'][$source .':'. $name])) {
       if (isset($filtered_components['sources'][$source .':'. $name])) {
         $row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
         $row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
       }
       }
@@ -447,9 +487,11 @@ function drush_features_export() {
       return drush_set_error('', 'No components supplied.');
       return drush_set_error('', 'No components supplied.');
     }
     }
     $components = _drush_features_component_list();
     $components = _drush_features_component_list();
-    $options = array(
-      'exported' => FALSE,
-    );
+    $options = array();
+
+    if (!drush_get_option('ignore-conflicts', FALSE)) {
+      $options['exported'] = FALSE;
+    }
 
 
     $filtered_components = _drush_features_component_filter($components, $args, $options);
     $filtered_components = _drush_features_component_filter($components, $args, $options);
     $items = $filtered_components['components'];
     $items = $filtered_components['components'];
@@ -579,9 +621,40 @@ function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
       drush_op('mkdir', $directory);
       drush_op('mkdir', $directory);
     }
     }
     if (is_dir($directory)) {
     if (is_dir($directory)) {
+      // Ensure that the export will be created in the English language.
+      // The export language must be set before flushing caches as that can
+      // result into translatables being statically cached.
+      $language = _features_export_language();
+
       drupal_flush_all_caches();
       drupal_flush_all_caches();
       $export = _drush_features_generate_export($info, $module_name);
       $export = _drush_features_generate_export($info, $module_name);
       $files = features_export_render($export, $module_name, TRUE);
       $files = features_export_render($export, $module_name, TRUE);
+
+      // Restore the language
+      _features_export_language($language);
+
+      // Copy any files if _files key is there.
+      if (!empty($files['_files'])) {
+        foreach ($files['_files'] as $file_name => $file_info) {
+          // See if files are in a sub directory.
+          if (strpos($file_name, '/')) {
+            $file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
+            if (!is_dir($file_directory)) {
+              drush_op('mkdir', $file_directory);
+            }
+          }
+          if (!empty($file_info['file_path'])) {
+            drush_op('file_unmanaged_copy', $file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
+          }
+          elseif (!empty($file_info['file_content'])) {
+            drush_op('file_put_contents', "{$directory}/{$file_name}", $file_info['file_content']);
+          }
+          else {
+            drush_log(dt("Entry for @file_name.in !module is invalid. ", array('!module' => $module_name, '@file_name' => $file_name)), 'ok');
+          }
+        }
+        unset($files['_files']);
+      }
       foreach ($files as $extension => $file_contents) {
       foreach ($files as $extension => $file_contents) {
         if (!in_array($extension, array('module', 'info'))) {
         if (!in_array($extension, array('module', 'info'))) {
           $extension .= '.inc';
           $extension .= '.inc';
@@ -635,7 +708,7 @@ function _drush_features_generate_export(&$info, &$module_name) {
       }
       }
       else {
       else {
         // Split version number parts.
         // Split version number parts.
-        $pattern = '/([0-9]-[a-z]+([0-9])+)/';
+        $pattern = '/([0-9]-[a-z]+([0-9]+))/';
         $matches = array();
         $matches = array();
         preg_match($pattern, $version_minor, $matches);
         preg_match($pattern, $version_minor, $matches);
         $number = array_pop($matches);
         $number = array_pop($matches);
@@ -747,7 +820,7 @@ function drush_features_revert() {
     }
     }
   }
   }
   else {
   else {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
     return;
   }
   }
 }
 }
@@ -801,7 +874,7 @@ function drush_features_revert_all() {
  */
  */
 function drush_features_diff() {
 function drush_features_diff() {
   if (!$args = func_get_args()) {
   if (!$args = func_get_args()) {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
     return;
   }
   }
   $module = $args[0];
   $module = $args[0];
@@ -886,6 +959,62 @@ function drush_features_diff() {
   }
   }
 }
 }
 
 
+/**
+ * Diff all enabled features that are not in their default state.
+ *
+ * @param ...
+ *   (Optional) A list of features to exclude from being reverted.
+ */
+function drush_features_diff_all() {
+  module_load_include('inc', 'features', 'features.export');
+  $features_to_exclude = func_get_args();
+
+  $features_to_revert = array();
+  foreach (features_get_features(NULL, TRUE) as $module) {
+    if ($module->status && !in_array($module->name, $features_to_exclude)) {
+      switch (features_get_storage($module->name)) {
+        case FEATURES_OVERRIDDEN:
+        case FEATURES_NEEDS_REVIEW:
+        case FEATURES_REBUILDABLE:
+          $features_to_diff[] = $module->name;
+          break;
+      }
+    }
+  }
+
+  if ($features_to_diff) {
+
+    // Check if the user wants to apply the force option.
+    $force = drush_get_option('force');
+
+    if($force) {
+      foreach ($features_to_diff as $module) {
+
+        drush_print(dt('Diff for !module:', array('!module' => $module)));
+
+        drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
+      }
+    }
+    else {
+
+      drush_print(dt('A diff will be performed for the following modules: !modules',
+        array('!modules' => implode(', ', $features_to_diff))
+      ));
+
+      if (drush_confirm(dt('Do you want to continue?'))) {
+        foreach ($features_to_diff as $module) {
+          if (drush_confirm(dt('Diff !module?', array('!module' => $module)))) {
+            drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
+          }
+        }
+      }
+      else {
+        return drush_user_abort('Aborting.');
+      }
+    }
+  }
+}
+
 /**
 /**
  * Helper function to call drush_set_error().
  * Helper function to call drush_set_error().
  *
  *

+ 169 - 14
sites/all/modules/contrib/admin/features/features.export.inc

@@ -45,6 +45,9 @@ function features_populate($info, $module_name) {
  * @return fully populated $export array.
  * @return fully populated $export array.
  */
  */
 function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
 function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
+  // Ensure that the export will be created in the english language.
+  $language = _features_export_language();
+
   if ($reset) {
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
     drupal_static_reset(__FUNCTION__);
   }
   }
@@ -89,6 +92,7 @@ function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE)
       }
       }
     }
     }
   }
   }
+  _features_export_language($language);
   return $export;
   return $export;
 }
 }
 
 
@@ -295,6 +299,11 @@ function features_export_render($export, $module_name, $reset = FALSE) {
     }
     }
 
 
     foreach ($hooks as $hook_name => $hook_info) {
     foreach ($hooks as $hook_name => $hook_info) {
+      // These are purely files that will be copied over.
+      if (is_array($hook_info) && (!empty($hook_info['file_path']) || !empty($hook_info['file_content']))) {
+        $code['_files'][$hook_name] = $hook_info;
+        continue;
+      }
       $hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info;
       $hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info;
       $hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : '';
       $hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : '';
       $hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name'];
       $hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name'];
@@ -305,7 +314,17 @@ function features_export_render($export, $module_name, $reset = FALSE) {
   // Finalize strings to be written to files
   // Finalize strings to be written to files
   $code = array_filter($code);
   $code = array_filter($code);
   foreach ($code as $filename => $contents) {
   foreach ($code as $filename => $contents) {
-    $code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
+    if ($filename != '_files') {
+      $code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
+    }
+  }
+
+  // Allow extra files be added to feature.
+  if ($files =  module_invoke_all('features_export_files', $module_name, $export)) {
+    $code['_files'] = !empty($code['_files']) ? $code['_files'] + $files : $files;
+  }
+  if (!empty($code['_files'])) {
+    drupal_alter('features_export_files', $code['_files'], $module_name, $export);
   }
   }
 
 
   // Generate info file output
   // Generate info file output
@@ -394,8 +413,8 @@ function features_detect_overrides($module) {
     $overridden = array();
     $overridden = array();
 
 
     // Compare feature info
     // Compare feature info
-    _features_sanitize($module->info);
-    _features_sanitize($export);
+    features_sanitize($module->info);
+    features_sanitize($export);
 
 
     $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
     $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
     if ($compare['normal'] !== $compare['default']) {
     if ($compare['normal'] !== $compare['default']) {
@@ -408,8 +427,8 @@ function features_detect_overrides($module) {
       if ($state != FEATURES_DEFAULT) {
       if ($state != FEATURES_DEFAULT) {
         $normal = features_get_normal($component, $module->name);
         $normal = features_get_normal($component, $module->name);
         $default = features_get_default($component, $module->name);
         $default = features_get_default($component, $module->name);
-        _features_sanitize($normal);
-        _features_sanitize($default);
+        features_sanitize($normal, $component);
+        features_sanitize($default, $component);
 
 
         $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
         $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
         if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
         if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
@@ -663,8 +682,7 @@ function features_get_signature($state = 'default', $module_name, $component, $r
       break;
       break;
   }
   }
   if (!empty($objects)) {
   if (!empty($objects)) {
-    $objects = (array) $objects;
-    _features_sanitize($objects);
+    features_sanitize($objects, $component);
     return md5(_features_linetrim(features_var_export($objects)));
     return md5(_features_linetrim(features_var_export($objects)));
   }
   }
   return FALSE;
   return FALSE;
@@ -721,7 +739,7 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
 
 
     // Special handling for dependencies component.
     // Special handling for dependencies component.
     if ($component === 'dependencies') {
     if ($component === 'dependencies') {
-      $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array();
+      $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], '_features_module_exists') : array();
     }
     }
     // All other components.
     // All other components.
     else {
     else {
@@ -739,6 +757,17 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
   return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
   return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
 }
 }
 
 
+/**
+ * Helper function to determine if a module is enabled
+ * @param $module
+ *   This module name comes from the .info file and can have version info in it.
+ */
+function _features_module_exists($module) {
+  $parsed_dependency = drupal_parse_dependency($module);
+  $name = $parsed_dependency['name'];
+  return module_exists($name);
+}
+
 /**
 /**
  * Get defaults for a given module/component pair.
  * Get defaults for a given module/component pair.
  */
  */
@@ -815,6 +844,13 @@ function features_get_default($component, $module_name = NULL, $alter = TRUE, $r
 
 
 /**
 /**
  * Get a map of components to their providing modules.
  * Get a map of components to their providing modules.
+ *
+ * @param string $component
+ * @param string $attribute
+ * @param callable $callback
+ * @param bool $reset
+ *
+ * @return array|bool
  */
  */
 function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
 function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
   $map = &drupal_static(__FUNCTION__, array());
   $map = &drupal_static(__FUNCTION__, array());
@@ -863,6 +899,9 @@ function features_get_default_map($component, $attribute = NULL, $callback = NUL
  * Retrieve an array of features/components and their current states.
  * Retrieve an array of features/components and their current states.
  */
  */
 function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
 function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
+  // Ensure that the export will be created in the English language.
+  $language = _features_export_language();
+
   if ($reset) {
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
     drupal_static_reset(__FUNCTION__);
   }
   }
@@ -873,7 +912,7 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
 
 
   // Retrieve only rebuildable components if requested.
   // Retrieve only rebuildable components if requested.
   features_include();
   features_include();
-  $components = array_keys(features_get_components());
+  $components = array_keys(features_get_components(NULL, NULL, $reset));
   if ($rebuild_only) {
   if ($rebuild_only) {
     foreach ($components as $k => $component) {
     foreach ($components as $k => $component) {
       if (!features_hook($component, 'features_rebuild')) {
       if (!features_hook($component, 'features_rebuild')) {
@@ -956,6 +995,9 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
   foreach ($return as $k => $v) {
   foreach ($return as $k => $v) {
     $return[$k] = array_intersect_key($return[$k], array_flip($components));
     $return[$k] = array_intersect_key($return[$k], array_flip($components));
   }
   }
+
+  _features_export_language($language);
+
   return $return;
   return $return;
 }
 }
 
 
@@ -970,25 +1012,49 @@ function _features_linetrim($code) {
   return implode("\n", $code);
   return implode("\n", $code);
 }
 }
 
 
+/**
+ * Helper function to "sanitize" an array or object.
+ * Converts everything to an array, sorts the keys, removes recursion.
+ * @param $array
+ * @param $component string name of component
+ * @param bool $remove_empty if set, remove null or empty values for assoc arrays.
+ */
+function features_sanitize(&$array, $component = NULL, $remove_empty = TRUE) {
+  $array = features_remove_recursion($array);
+  if (isset($component)) {
+    $ignore_keys = _features_get_ignore_keys($component);
+    // remove keys to be ignored
+    if (count($ignore_keys)) {
+      _features_remove_ignores($array, $ignore_keys);
+    }
+  }
+  _features_sanitize($array, $remove_empty);
+}
+
 /**
 /**
  * "Sanitizes" an array recursively, performing two key operations:
  * "Sanitizes" an array recursively, performing two key operations:
  * - Sort an array by its keys (assoc) or values (non-assoc)
  * - Sort an array by its keys (assoc) or values (non-assoc)
- * - Remove any null or empty values for associative arrays (array_filter()).
+ * @param bool $remove_empty if set, remove null or empty values for assoc arrays.
  */
  */
-function _features_sanitize(&$array) {
+function _features_sanitize(&$array, $remove_empty = TRUE) {
+  if (is_object($array)) {
+    $array = get_object_vars($array);
+  }
   if (is_array($array)) {
   if (is_array($array)) {
     $is_assoc = _features_is_assoc($array);
     $is_assoc = _features_is_assoc($array);
     if ($is_assoc) {
     if ($is_assoc) {
       ksort($array, SORT_STRING);
       ksort($array, SORT_STRING);
-      $array = array_filter($array);
+      if ($remove_empty) {
+        $array = array_filter($array);
+      }
     }
     }
     else {
     else {
       sort($array);
       sort($array);
     }
     }
     foreach ($array as $k => $v) {
     foreach ($array as $k => $v) {
-      if (is_array($v)) {
+      if (is_array($v) or is_object($v)) {
         _features_sanitize($array[$k]);
         _features_sanitize($array[$k]);
-        if ($is_assoc && empty($array[$k])) {
+        if ($remove_empty && $is_assoc && empty($array[$k])) {
           unset($array[$k]);
           unset($array[$k]);
         }
         }
       }
       }
@@ -1010,3 +1076,92 @@ function _features_sanitize(&$array) {
 function _features_is_assoc($array) {
 function _features_is_assoc($array) {
   return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0));
   return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0));
 }
 }
+
+/**
+ * Removes recursion from an object or array.
+ *
+ * Taken from https://code.google.com/p/formaldehyde/source/browse/trunk/formaldehyde.php
+ * Also used in node_export module
+ *
+ * @param $o mixed
+ * @return mixed
+ *   returns a copy of the object or array with recursion removed
+ */
+function features_remove_recursion($o) {
+  static $replace;
+  if (!isset($replace)) {
+    $replace = create_function(
+      '$m',
+      '$r="\x00{$m[1]}ecursion_features";return \'s:\'.strlen($r.$m[2]).\':"\'.$r.$m[2].\'";\';'
+    );
+  }
+  if (is_array($o) || is_object($o)) {
+    $re = '#(r|R):([0-9]+);#';
+    $serialize = serialize($o);
+    if (preg_match($re, $serialize)) {
+      $last = $pos = 0;
+      while (false !== ($pos = strpos($serialize, 's:', $pos))) {
+        $chunk = substr($serialize, $last, $pos - $last);
+        if (preg_match($re, $chunk)) {
+          $length = strlen($chunk);
+          $chunk = preg_replace_callback($re, $replace, $chunk);
+          $serialize = substr($serialize, 0, $last) . $chunk . substr($serialize, $last + ($pos - $last));
+          $pos += strlen($chunk) - $length;
+        }
+        $pos += 2;
+        $last = strpos($serialize, ':', $pos);
+        $length = substr($serialize, $pos, $last - $pos);
+        $last += 4 + $length;
+        $pos = $last;
+      }
+      $serialize = substr($serialize, 0, $last) . preg_replace_callback($re, $replace, substr($serialize, $last));
+      $o = unserialize($serialize);
+    }
+  }
+  return $o;
+}
+
+/**
+ * Helper to removes a set of keys an object/array.
+ *
+ * @param $item
+ *   An object or array passed by reference.
+ * @param $ignore_keys
+ *   Array of keys to be ignored. Values are the level of the key.
+ * @param $level
+ *   Level of key to remove.  Up to 2 levels deep because $item can still be
+ *   recursive
+ */
+function _features_remove_ignores(&$item, $ignore_keys, $level = -1) {
+  $is_object = is_object($item);
+  if (!is_array($item) && !is_object($item)) {
+    return;
+  }
+  foreach ($item as $key => &$value) {
+    if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
+      if ($is_object) {
+        unset($item->$key);
+      }
+      else {
+        unset($item[$key]);
+      }
+    }
+    elseif (($level < 2) && (is_array($value) || is_object($value))) {
+      _features_remove_ignores($value, $ignore_keys, $level+1);
+    }
+  }
+  unset($value);
+}
+
+/**
+ * Returns an array of keys to be ignored for various exportables
+ * @param $component
+ *   The component to retrieve ignore_keys from.
+ */
+function _features_get_ignore_keys($component) {
+  static $cache;
+  if (!isset($cache[$component])) {
+    $cache[$component] = module_invoke_all('features_ignore', $component);
+  }
+  return $cache[$component];
+}

+ 7 - 3
sites/all/modules/contrib/admin/features/features.info

@@ -3,12 +3,16 @@ description = "Provides feature management for Drupal."
 core = 7.x
 core = 7.x
 package = "Features"
 package = "Features"
 files[] = tests/features.test
 files[] = tests/features.test
+test_dependencies[] = image
+test_dependencies[] = strongarm
+test_dependencies[] = taxonomy
+test_dependencies[] = views
 
 
 configure = admin/structure/features/settings
 configure = admin/structure/features/settings
 
 
-; Information added by Drupal.org packaging script on 2015-01-05
-version = "7.x-2.3"
+; Information added by Drupal.org packaging script on 2016-04-18
+version = "7.x-2.10"
 core = "7.x"
 core = "7.x"
 project = "features"
 project = "features"
-datestamp = "1420492083"
+datestamp = "1461011641"
 
 

+ 27 - 4
sites/all/modules/contrib/admin/features/features.install

@@ -5,6 +5,15 @@
  * Install, update and uninstall functions for the features module.
  * Install, update and uninstall functions for the features module.
  */
  */
 
 
+/**
+ * Implements hook_schema().
+ */
+function features_schema() {
+  $schema['cache_features'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_features']['description'] = 'Cache table for features to store module info.';
+  return $schema;
+}
+
 /**
 /**
  * Implements hook_install().
  * Implements hook_install().
  */
  */
@@ -27,10 +36,14 @@ function features_uninstall() {
   variable_del('features_ignored_orphans');
   variable_del('features_ignored_orphans');
   variable_del('features_feature_locked');
   variable_del('features_feature_locked');
   variable_del('features_lock_mode');
   variable_del('features_lock_mode');
-  foreach (array_keys(features_get_components()) as $component) {
-    variable_del('features_admin_show_component_' . $component);
-    variable_del('features_component_locked_' . $component);
-  }
+
+  db_delete('variable')
+    ->condition('name', 'features_admin_show_component_%', 'LIKE')
+    ->execute();
+  db_delete('variable')
+    ->condition('name', 'features_component_locked_%', 'LIKE')
+    ->execute();
+
   if (db_table_exists('menu_custom')) {
   if (db_table_exists('menu_custom')) {
     db_delete('menu_custom')
     db_delete('menu_custom')
       ->condition('menu_name', 'features')
       ->condition('menu_name', 'features')
@@ -126,3 +139,13 @@ function features_update_6101() {
   }
   }
   return array();
   return array();
 }
 }
+
+/**
+ * Add {cache_features} table.
+ */
+function features_update_7200() {
+  if (!db_table_exists('cache_features')) {
+    $schema = drupal_get_schema_unprocessed('system', 'cache');
+    db_create_table('cache_features', $schema);
+  }
+}

+ 7 - 5
sites/all/modules/contrib/admin/features/features.js

@@ -128,10 +128,12 @@ jQuery.fn.sortElements = (function(){
             if (!$(this).hasClass('features-checkall')) {
             if (!$(this).hasClass('features-checkall')) {
               var key = $(this).attr('name');
               var key = $(this).attr('name');
               var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
               var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
-              var component = matches[1];
-              var item = matches[4];
-              if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
-                $(this).parent().addClass('features-conflict');
+              if (matches != null) {
+                var component = matches[1];
+                var item = matches[4];
+                if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
+                  $(this).parent().addClass('features-conflict');
+                }
               }
               }
             }
             }
           });
           });
@@ -290,7 +292,7 @@ jQuery.fn.sortElements = (function(){
       }
       }
 
 
       // Handle component selection UI
       // Handle component selection UI
-      $('#features-export-wrapper input[type=checkbox]', context).click(function() {
+      $('#features-export-wrapper input[type=checkbox]:not(.processed)', context).addClass('processed').click(function() {
         _resetTimeout();
         _resetTimeout();
         if ($(this).hasClass('component-select')) {
         if ($(this).hasClass('component-select')) {
           moveCheckbox(this, 'added', true);
           moveCheckbox(this, 'added', true);

+ 111 - 19
sites/all/modules/contrib/admin/features/features.module

@@ -84,8 +84,7 @@ function features_menu() {
   $items['admin/structure/features/cleanup'] = array(
   $items['admin/structure/features/cleanup'] = array(
     'title' => 'Cleanup',
     'title' => 'Cleanup',
     'description' => 'Clear cache after enabling/disabling a feature.',
     'description' => 'Clear cache after enabling/disabling a feature.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('features_cleanup_form', 4),
+    'page callback' => 'features_cleanup',
     'type' => MENU_CALLBACK,
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
     'file' => 'features.admin.inc',
     'weight' => 1,
     'weight' => 1,
@@ -128,7 +127,7 @@ function features_menu() {
     'description' => 'Display components of a feature.',
     'description' => 'Display components of a feature.',
     'page callback' => 'drupal_get_form',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_admin_components', 3),
     'page arguments' => array('features_admin_components', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
     'type' => MENU_CALLBACK,
@@ -147,7 +146,7 @@ function features_menu() {
     'description' => 'Recreate an existing feature.',
     'description' => 'Recreate an existing feature.',
     'page callback' => 'drupal_get_form',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_export_form', 3),
     'page arguments' => array('features_export_form', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'access arguments' => array('administer features'),
     'type' => MENU_LOCAL_TASK,
     'type' => MENU_LOCAL_TASK,
@@ -160,7 +159,7 @@ function features_menu() {
       'description' => 'Compare default and current feature.',
       'description' => 'Compare default and current feature.',
       'page callback' => 'features_feature_diff',
       'page callback' => 'features_feature_diff',
       'page arguments' => array(3, 5),
       'page arguments' => array(3, 5),
-      'load arguments' => array(3, TRUE),
+      'load arguments' => array(TRUE),
       'access callback' => 'features_access_override_actions',
       'access callback' => 'features_access_override_actions',
       'access arguments' => array(3),
       'access arguments' => array(3),
       'type' => MENU_LOCAL_TASK,
       'type' => MENU_LOCAL_TASK,
@@ -173,7 +172,7 @@ function features_menu() {
     'description' => 'Lock a feature or components.',
     'description' => 'Lock a feature or components.',
     'page callback' => 'features_admin_lock',
     'page callback' => 'features_admin_lock',
     'page arguments' => array(3, 5, 6),
     'page arguments' => array(3, 5, 6),
-    'load arguments' => array(3, TRUE, TRUE),
+    'load arguments' => array(TRUE),
     'access arguments' => array('administer features'),
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
     'file' => 'features.admin.inc',
@@ -183,7 +182,7 @@ function features_menu() {
     'description' => 'Javascript status call back.',
     'description' => 'Javascript status call back.',
     'page callback' => 'features_feature_status',
     'page callback' => 'features_feature_status',
     'page arguments' => array(3),
     'page arguments' => array(3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
     'type' => MENU_CALLBACK,
@@ -268,13 +267,20 @@ function features_theme() {
  * Implements hook_flush_caches().
  * Implements hook_flush_caches().
  */
  */
 function features_flush_caches() {
 function features_flush_caches() {
-  if (variable_get('features_rebuild_on_flush', TRUE)) {
+  if (($modules_changed = variable_get('features_modules_changed', FALSE)) || variable_get('features_rebuild_on_flush', TRUE)) {
+    if ($modules_changed) {
+      variable_set('features_modules_changed', FALSE);
+    }
     features_rebuild();
     features_rebuild();
     // Don't flush the modules cache during installation, for performance reasons.
     // Don't flush the modules cache during installation, for performance reasons.
     if (variable_get('install_task') == 'done') {
     if (variable_get('install_task') == 'done') {
       features_get_modules(NULL, TRUE);
       features_get_modules(NULL, TRUE);
     }
     }
   }
   }
+
+  if (db_table_exists('cache_features')) {
+    return array('cache_features');
+  }
   return array();
   return array();
 }
 }
 
 
@@ -349,6 +355,15 @@ function features_modules_disabled($modules) {
  * Implements hook_modules_enabled().
  * Implements hook_modules_enabled().
  */
  */
 function features_modules_enabled($modules) {
 function features_modules_enabled($modules) {
+  // Allow distributions to disable this behavior and rebuild the features
+  // manually inside a batch.
+  if (!variable_get('features_rebuild_on_module_install', TRUE)) {
+    return;
+  }
+
+  // mark modules as being changed for test in features_flush_caches
+  variable_set('features_modules_changed', TRUE);
+
   // Go through all modules and gather features that can be enabled.
   // Go through all modules and gather features that can be enabled.
   $items = array();
   $items = array();
   foreach ($modules as $module) {
   foreach ($modules as $module) {
@@ -385,7 +400,7 @@ function features_include($reset = FALSE) {
     // Features provides integration on behalf of these modules.
     // Features provides integration on behalf of these modules.
     // The features include provides handling for the feature dependencies.
     // The features include provides handling for the feature dependencies.
     // Note that ctools is placed last because it implements hooks "dynamically" for other modules.
     // Note that ctools is placed last because it implements hooks "dynamically" for other modules.
-    $modules = array('features', 'block', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
+    $modules = array('features', 'block', 'contact', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
 
 
     foreach (array_filter($modules, 'module_exists') as $module) {
     foreach (array_filter($modules, 'module_exists') as $module) {
       module_load_include('inc', 'features', "includes/features.$module");
       module_load_include('inc', 'features', "includes/features.$module");
@@ -503,7 +518,7 @@ function features_load_feature($name, $reset = FALSE) {
  * Return a module 'object' including .info information.
  * Return a module 'object' including .info information.
  *
  *
  * @param $name
  * @param $name
- *   The name of the module to retrieve information for. If ommitted,
+ *   The name of the module to retrieve information for. If omitted,
  *   an array of all available modules will be returned.
  *   an array of all available modules will be returned.
  * @param $reset
  * @param $reset
  *   Whether to reset the cache.
  *   Whether to reset the cache.
@@ -535,13 +550,13 @@ function features_get_components($component = NULL, $key = NULL, $reset = FALSE)
 
 
   if ($reset || !isset($components) || !isset($component_by_key)) {
   if ($reset || !isset($components) || !isset($component_by_key)) {
     $components = $component_by_key = array();
     $components = $component_by_key = array();
-    if (!$reset && ($cache = cache_get('features_api'))) {
+    if (!$reset && ($cache = cache_get('features_api', 'cache_features'))) {
       $components = $cache->data;
       $components = $cache->data;
     }
     }
     else {
     else {
       $components = module_invoke_all('features_api');
       $components = module_invoke_all('features_api');
       drupal_alter('features_api', $components);
       drupal_alter('features_api', $components);
-      cache_set('features_api', $components);
+      cache_set('features_api', $components, 'cache_features');
     }
     }
 
 
     foreach ($components as $component_type => $component_information) {
     foreach ($components as $component_type => $component_information) {
@@ -607,6 +622,8 @@ function features_hook($component, $hook, $reset = FALSE) {
  *   Clear the module info cache.
  *   Clear the module info cache.
  */
  */
 function features_install_modules($modules) {
 function features_install_modules($modules) {
+  variable_set('features_modules_changed', TRUE);
+
   module_load_include('inc', 'features', 'features.export');
   module_load_include('inc', 'features', 'features.export');
   $files = system_rebuild_module_data();
   $files = system_rebuild_module_data();
 
 
@@ -650,7 +667,7 @@ function features_get_features($name = NULL, $reset = FALSE) {
 function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
 function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
   static $cache;
   static $cache;
   if (!isset($cache)) {
   if (!isset($cache)) {
-    $cache = cache_get('features_module_info');
+    $cache = cache_get('features_module_info', 'cache_features');
   }
   }
   if (empty($cache) || $reset) {
   if (empty($cache) || $reset) {
     $data = array(
     $data = array(
@@ -738,14 +755,14 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
     $data['feature'] = $sorted;
     $data['feature'] = $sorted;
 
 
     variable_set('features_ignored_orphans', $ignored);
     variable_set('features_ignored_orphans', $ignored);
-    cache_set("features_module_info", $data);
+    cache_set('features_module_info', $data, 'cache_features');
     $cache = new stdClass();
     $cache = new stdClass();
     $cache->data = $data;
     $cache->data = $data;
   }
   }
   if (!empty($name)) {
   if (!empty($name)) {
-    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : array();
+    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : FALSE;
   }
   }
-  return !empty($cache->data[$type]) ? $cache->data[$type] : array();
+  return !empty($cache->data[$type]) ? $cache->data[$type] : FALSE;
 }
 }
 
 
 /**
 /**
@@ -892,7 +909,7 @@ function features_access_override_actions($feature) {
 
 
       features_include();
       features_include();
       module_load_include('inc', 'features', 'features.export');
       module_load_include('inc', 'features', 'features.export');
-      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW)) && user_access('administer features');
+      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_DEFAULT, FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW));
     }
     }
     return $access[$feature->name];
     return $access[$feature->name];
   }
   }
@@ -903,7 +920,9 @@ function features_access_override_actions($feature) {
  * Implements hook_form_alter() for system_modules form().
  * Implements hook_form_alter() for system_modules form().
  */
  */
 function features_form_system_modules_alter(&$form) {
 function features_form_system_modules_alter(&$form) {
-  features_rebuild();
+  if (variable_get('features_rebuild_modules_page', FALSE)) {
+    features_rebuild();
+  }
 }
 }
 
 
 /**
 /**
@@ -1067,6 +1086,7 @@ function features_hook_info() {
     'features_api',
     'features_api',
     'features_pipe_alter',
     'features_pipe_alter',
     'features_export_alter',
     'features_export_alter',
+    'features_export_options_alter',
   );
   );
   return array_fill_keys($hooks, array('group' => 'features'));
   return array_fill_keys($hooks, array('group' => 'features'));
 }
 }
@@ -1075,6 +1095,9 @@ function features_hook_info() {
  * Change vocabularies permission, from vocab id to machine name and vice versa.
  * Change vocabularies permission, from vocab id to machine name and vice versa.
  */
  */
 function _user_features_change_term_permission(&$perm, $type = 'vid') {
 function _user_features_change_term_permission(&$perm, $type = 'vid') {
+  if (!module_exists('taxonomy')) {
+    return;
+  }
   // Export vocabulary permissions using the machine name, instead of vocabulary
   // Export vocabulary permissions using the machine name, instead of vocabulary
   // id.
   // id.
   if (strpos($perm, 'edit terms in ') !== FALSE || strpos($perm, 'delete terms in ') !== FALSE) {
   if (strpos($perm, 'edit terms in ') !== FALSE || strpos($perm, 'delete terms in ') !== FALSE) {
@@ -1186,7 +1209,6 @@ function features_feature_lock($feature, $component = NULL) {
   variable_set('features_feature_locked', $locked);
   variable_set('features_feature_locked', $locked);
 }
 }
 
 
-
 /**
 /**
  * Unlocks a feature or it's component.
  * Unlocks a feature or it's component.
  */
  */
@@ -1200,3 +1222,73 @@ function features_feature_unlock($feature, $component = NULL) {
   }
   }
   variable_set('features_feature_locked', $locked);
   variable_set('features_feature_locked', $locked);
 }
 }
+
+/**
+ * Sets/Returns the current language to english to ensure a proper export.
+ */
+function _features_export_language($language = NULL) {
+  $current = $GLOBALS['language'];
+  if (isset($language)) {
+    $GLOBALS['language'] = $language;
+  }
+  elseif ($GLOBALS['language']->language != 'en') {
+    // Create the language object as language_default() does.
+    $GLOBALS['language'] = (object) array(
+      'language' => 'en',
+      'name' => 'English',
+      'native' => 'English',
+      'direction' => 0,
+      'enabled' => 1,
+      'plurals' => 0,
+      'formula' => '',
+      'domain' => '',
+      'prefix' => '',
+      'weight' => 0,
+      'javascript' => '',
+    );
+  }
+  return $current;
+}
+
+/**
+ * Implements hook_features_ignore().
+ */
+function features_features_ignore($component) {
+  // Determine which keys need to be ignored for override diff for various components.
+  // Value is how many levels deep the key is.
+  $ignores = array();
+  switch ($component) {
+    case 'views_view':
+      $ignores['current_display'] = 0;
+      $ignores['display_handler'] = 0;
+      $ignores['handler'] = 2;
+      $ignores['query'] = 0;
+      $ignores['localization_plugin'] = 0;
+      // Views automatically adds these two on export to set values.
+      $ignores['api_version'] = 0;
+      $ignores['disabled'] = 0;
+      break;
+    case 'image':
+      $ignores['module'] = 0;
+      $ignores['name'] = 0;
+      $ignores['storage'] = 0;
+      // Various properties are loaded into the effect in image_styles.
+      $ignores['summary theme'] = 2;
+      $ignores['module'] = 2;
+      $ignores['label'] = 2;
+      $ignores['help'] = 2;
+      $ignores['form callback'] = 2;
+      $ignores['effect callback'] = 2;
+      $ignores['dimensions callback'] = 2;
+      break;
+    case 'field':
+      $ignores['locked'] = 1;
+      break;
+    case 'field_base':
+      $ignores['indexes'] = 0;
+      break;
+    case 'taxonomy':
+      $ignores['hierarchy'] = 0;
+  }
+  return $ignores;
+}

+ 115 - 0
sites/all/modules/contrib/admin/features/includes/features.contact.inc

@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Features integration for 'contact' module.
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function contact_features_api() {
+  return array(
+    'contact_categories' => array(
+      'name' => t('Contact categories'),
+      'feature_source' => TRUE,
+      'default_hook' => 'contact_categories_defaults',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED,
+    ),
+  );
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function contact_categories_features_export_options() {
+  $options = array();
+  $categories = db_select('contact', 'c')->fields('c')->execute()->fetchAll();
+  foreach ($categories as $category) {
+    $options["$category->category"] = "$category->category";
+  }
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function contact_categories_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['contact'] = 'contact';
+
+  foreach ($data as $name) {
+    $export['features']['contact_categories'][$name] = $name;
+  }
+
+  return array();
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function contact_categories_features_export_render($module, $data, $export = NULL) {
+  $render = array();
+  foreach ($data as $name) {
+    $export_category = db_select('contact', 'c')
+      ->fields('c', array('cid', 'category'))
+      ->condition('category', $name, 'LIKE')
+      ->execute()
+      ->fetchAll();
+    if (isset($export_category[0]->cid) && ($category = contact_load($export_category[0]->cid))) {
+      unset($category['cid']);
+      $render[$name] = $category;
+    }
+  }
+  return array('contact_categories_defaults' => '  return ' . features_var_export($render, '  ') . ';');
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function contact_categories_features_revert($module) {
+  return contact_categories_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function contact_categories_features_rebuild($module) {
+  if ($defaults = features_get_default('contact_categories', $module)) {
+    foreach ($defaults as $default_category) {
+      $existing_categories = db_select('contact', 'c')
+        ->fields('c', array('cid', 'category'))
+        ->execute()
+        ->fetchAll();
+      if ($existing_categories) {
+        foreach ($existing_categories as $existing_category) {
+          if ($default_category['category'] == $existing_category->category) {
+            db_update('contact')
+              ->fields(
+                array(
+                  'recipients' => $default_category['recipients'],
+                  'reply' => $default_category['reply'],
+                  'weight' => $default_category['weight'],
+                  'selected' => $default_category['selected'],
+                )
+              )
+              ->condition('category', $existing_category->category, '=')
+              ->execute();
+          }
+          else {
+            db_merge('contact')
+              ->key(array('category' => $default_category['category']))
+              ->fields($default_category)
+              ->execute();
+          }
+        }
+      }
+      else {
+        db_merge('contact')
+          ->key(array('category' => $default_category['category']))
+          ->fields($default_category)
+          ->execute();
+      }
+    }
+  }
+}

+ 56 - 9
sites/all/modules/contrib/admin/features/includes/features.field.inc

@@ -151,14 +151,14 @@ function field_base_features_export_render($module, $data, $export = NULL) {
   foreach ($data as $identifier) {
   foreach ($data as $identifier) {
     if ($field = features_field_base_load($identifier)) {
     if ($field = features_field_base_load($identifier)) {
       unset($field['columns']);
       unset($field['columns']);
-      // unset($field['locked']);
+      unset($field['foreign keys']);
       // Only remove the 'storage' declaration if the field is using the default
       // Only remove the 'storage' declaration if the field is using the default
       // storage type.
       // storage type.
       if ($field['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
       if ($field['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
         unset($field['storage']);
         unset($field['storage']);
       }
       }
       // If we still have a storage declaration here it means that a non-default
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       // never need to change the 'details' key, so don't render it.
       if (isset($field['storage']['details'])) {
       if (isset($field['storage']['details'])) {
         unset($field['storage']['details']);
         unset($field['storage']['details']);
@@ -166,8 +166,15 @@ function field_base_features_export_render($module, $data, $export = NULL) {
 
 
       _field_instance_features_export_sort($field);
       _field_instance_features_export_sort($field);
       $field_export = features_var_export($field, '  ');
       $field_export = features_var_export($field, '  ');
+      $field_prefix = '  // Exported field_base: ';
       $field_identifier = features_var_export($identifier);
       $field_identifier = features_var_export($identifier);
-      $code[] = "  // Exported field_base: {$field_identifier}";
+      if (features_field_export_needs_wrap($field_prefix, $field_identifier)) {
+        $code[] = rtrim($field_prefix);
+        $code[] = "  // {$field_identifier}.";
+      }
+      else {
+        $code[] = $field_prefix . $field_identifier . '.';
+      }
       $code[] = "  \$field_bases[{$field_identifier}] = {$field_export};";
       $code[] = "  \$field_bases[{$field_identifier}] = {$field_export};";
       $code[] = "";
       $code[] = "";
     }
     }
@@ -190,8 +197,15 @@ function field_instance_features_export_render($module, $data, $export = NULL) {
     if ($instance = features_field_instance_load($identifier)) {
     if ($instance = features_field_instance_load($identifier)) {
       _field_instance_features_export_sort($instance);
       _field_instance_features_export_sort($instance);
       $field_export = features_var_export($instance, '  ');
       $field_export = features_var_export($instance, '  ');
+      $instance_prefix = '  // Exported field_instance: ';
       $instance_identifier = features_var_export($identifier);
       $instance_identifier = features_var_export($identifier);
-      $code[] = "  // Exported field_instance: {$instance_identifier}";
+      if (features_field_export_needs_wrap($instance_prefix, $instance_identifier)) {
+        $code[] = rtrim($instance_prefix);
+        $code[] = "  // {$instance_identifier}.";
+      }
+      else {
+        $code[] = $instance_prefix . $instance_identifier . '.';
+      }
       $code[] = "  \$field_instances[{$instance_identifier}] = {$field_export};";
       $code[] = "  \$field_instances[{$instance_identifier}] = {$field_export};";
       $code[] = "";
       $code[] = "";
 
 
@@ -260,12 +274,23 @@ function field_base_features_rebuild($module) {
       // Create or update field.
       // Create or update field.
       if (isset($existing_fields[$field['field_name']])) {
       if (isset($existing_fields[$field['field_name']])) {
         $existing_field = $existing_fields[$field['field_name']];
         $existing_field = $existing_fields[$field['field_name']];
-        if ($field + $existing_field !== $existing_field) {
-          field_update_field($field);
+        $array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
+          try {
+            field_update_field($field);
+          }
+          catch (FieldException $e) {
+            watchdog('features', 'Attempt to update field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+          }
         }
         }
       }
       }
       else {
       else {
-        field_create_field($field);
+        try {
+          field_create_field($field);
+        }
+        catch (FieldException $e) {
+          watchdog('features', 'Attempt to create field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+        }
         $existing_fields[$field['field_name']] = $field;
         $existing_fields[$field['field_name']] = $field;
       }
       }
       variable_set('menu_rebuild_needed', TRUE);
       variable_set('menu_rebuild_needed', TRUE);
@@ -395,7 +420,7 @@ function field_features_export_render($module, $data, $export = NULL) {
         unset($field['field_config']['storage']);
         unset($field['field_config']['storage']);
       }
       }
       // If we still have a storage declaration here it means that a non-default
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       // never need to change the 'details' key, so don't render it.
       if (isset($field['field_config']['storage']['details'])) {
       if (isset($field['field_config']['storage']['details'])) {
         unset($field['field_config']['storage']['details']);
         unset($field['field_config']['storage']['details']);
@@ -469,7 +494,8 @@ function field_features_rebuild($module) {
       $field_config = $field['field_config'];
       $field_config = $field['field_config'];
       if (isset($existing_fields[$field_config['field_name']])) {
       if (isset($existing_fields[$field_config['field_name']])) {
         $existing_field = $existing_fields[$field_config['field_name']];
         $existing_field = $existing_fields[$field_config['field_name']];
-        if ($field_config + $existing_field !== $existing_field) {
+        $array_diff_result = drupal_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
           try {
           try {
             field_update_field($field_config);
             field_update_field($field_config);
           }
           }
@@ -528,3 +554,24 @@ function features_field_load($identifier) {
   }
   }
   return FALSE;
   return FALSE;
 }
 }
+
+/**
+ * Determine if a field export line needs to be wrapped.
+ *
+ * Drupal code standards specify that comments should wrap at 80 characters or
+ * less.
+ *
+ * @param string $prefix
+ *   The prefix to be exported before the field identifier.
+ * @param string $identifier
+ *   The field identifier.
+ *
+ * @return BOOL
+ *   TRUE if the line should be wrapped after the prefix, else FALSE.
+ *
+ * @see https://www.drupal.org/node/1354
+ */
+function features_field_export_needs_wrap($prefix, $identifier) {
+  // Check for 79 characters, since the comment ends with a full stop.
+  return (strlen($prefix) + strlen($identifier) > 79);
+}

+ 20 - 11
sites/all/modules/contrib/admin/features/includes/features.image.inc

@@ -86,16 +86,25 @@ function image_features_revert($module) {
 /**
 /**
  * Remove unnecessary keys for export.
  * Remove unnecessary keys for export.
  */
  */
-function _image_features_style_sanitize(&$style, $child = FALSE) {
-  $omit = $child ? array('isid', 'ieid', 'storage') : array('isid', 'ieid', 'storage', 'module');
-  if (is_array($style)) {
-    foreach ($style as $k => $v) {
-      if (in_array($k, $omit, TRUE)) {
-        unset($style[$k]);
-      }
-      else if (is_array($v)) {
-        _image_features_style_sanitize($style[$k], TRUE);
-      }
-    }
+function _image_features_style_sanitize(array &$style) {
+  // Sanitize style: Don't export numeric IDs and things which get overwritten
+  // in image_styles() or are code/storage specific. The name property will be
+  // the key of the exported $style array.
+  $style = array_diff_key($style, array_flip(array(
+    'isid',
+    'name',
+    'module',
+    'storage',
+  )));
+
+  // Sanitize effects: all that needs to be kept is name, weight and data,
+  // which holds all the style-specific configuration. Other keys are assumed
+  // to belong to the definition of the effect itself, so not configuration.
+  foreach ($style['effects'] as $id => $effect) {
+    $style['effects'][$id] = array_intersect_key($effect, array_flip(array(
+      'name',
+      'data',
+      'weight',
+    )));
   }
   }
 }
 }

+ 3 - 2
sites/all/modules/contrib/admin/features/includes/features.locale.inc

@@ -134,14 +134,15 @@ function _features_language_save($language) {
       ->fields(array(
       ->fields(array(
         'plurals' => empty($language->plurals) ? 0 : $language->plurals,
         'plurals' => empty($language->plurals) ? 0 : $language->plurals,
         'formula' => empty($language->formula) ? '' : $language->formula,
         'formula' => empty($language->formula) ? '' : $language->formula,
+        'weight' => empty($language->weight) ? 0 : $language->weight,
       ))
       ))
       ->condition('language', $language->language)
       ->condition('language', $language->language)
       ->execute();
       ->execute();
   }
   }
   // Update Existing language.
   // Update Existing language.
   else {
   else {
-    // @TODO: get properties from schema.
-    $properties = array('language', 'name', 'native', 'direction', 'enabled', 'plurals', 'formula', 'domain', 'prefix', 'weight', 'javascript');
+    // Get field list from table schema.
+    $properties = drupal_schema_fields_sql('languages');
     // The javascript hash is not in the imported data but should be empty
     // The javascript hash is not in the imported data but should be empty
     if (!isset($language->javascript)) {
     if (!isset($language->javascript)) {
       $language->javascript = '';
       $language->javascript = '';

+ 3 - 3
sites/all/modules/contrib/admin/features/includes/features.menu.inc

@@ -103,7 +103,6 @@ function menu_custom_features_export_render($module, $data) {
     $code[] = features_translatables_export($translatables, '  ');
     $code[] = features_translatables_export($translatables, '  ');
   }
   }
 
 
-  $code[] = '';
   $code[] = '  return $menus;';
   $code[] = '  return $menus;';
   $code = implode("\n", $code);
   $code = implode("\n", $code);
   return array('menu_default_menu_custom' => $code);
   return array('menu_default_menu_custom' => $code);
@@ -248,16 +247,16 @@ function menu_links_features_export_render($module, $data, $export = NULL) {
        unset($link['plid']);
        unset($link['plid']);
        unset($link['mlid']);
        unset($link['mlid']);
 
 
-      $code[] = "  // Exported menu link: {$new_identifier}";
+      $code[] = "  // Exported menu link: {$new_identifier}.";
       $code[] = "  \$menu_links['{$new_identifier}'] = ". features_var_export($link, '  ') .";";
       $code[] = "  \$menu_links['{$new_identifier}'] = ". features_var_export($link, '  ') .";";
       $translatables[] = $link['link_title'];
       $translatables[] = $link['link_title'];
     }
     }
   }
   }
+  $code[] = '';
   if (!empty($translatables)) {
   if (!empty($translatables)) {
     $code[] = features_translatables_export($translatables, '  ');
     $code[] = features_translatables_export($translatables, '  ');
   }
   }
 
 
-  $code[] = '';
   $code[] = '  return $menu_links;';
   $code[] = '  return $menu_links;';
   $code = implode("\n", $code);
   $code = implode("\n", $code);
   return array('menu_default_menu_links' => $code);
   return array('menu_default_menu_links' => $code);
@@ -316,6 +315,7 @@ function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
     foreach ($unordered as $link) {
     foreach ($unordered as $link) {
       $identifier = menu_links_features_identifier($link);
       $identifier = menu_links_features_identifier($link);
       $ordered[$identifier] = 0;
       $ordered[$identifier] = 0;
+      $all_links[$identifier] = $link;
     }
     }
     asort($ordered);
     asort($ordered);
   }
   }

+ 15 - 4
sites/all/modules/contrib/admin/features/includes/features.node.inc

@@ -117,7 +117,7 @@ function node_features_revert($module = NULL) {
 }
 }
 
 
 /**
 /**
- * Implements hook_features_disable().
+ * Implements hook_features_disable_feature().
  *
  *
  * When a features module is disabled, modify any node types it provides so
  * When a features module is disabled, modify any node types it provides so
  * they can be deleted manually through the content types UI.
  * they can be deleted manually through the content types UI.
@@ -125,7 +125,7 @@ function node_features_revert($module = NULL) {
  * @param $module
  * @param $module
  *   Name of module that has been disabled.
  *   Name of module that has been disabled.
  */
  */
-function node_features_disable($module) {
+function node_features_disable_feature($module) {
   if ($default_types = features_get_default('node', $module)) {
   if ($default_types = features_get_default('node', $module)) {
     foreach ($default_types as $type_name => $type_info) {
     foreach ($default_types as $type_name => $type_info) {
       $type_info = node_type_load($type_name);
       $type_info = node_type_load($type_name);
@@ -133,22 +133,26 @@ function node_features_disable($module) {
       $type_info->custom = 1;
       $type_info->custom = 1;
       $type_info->modified = 1;
       $type_info->modified = 1;
       $type_info->locked = 0;
       $type_info->locked = 0;
+      $type_info->disabled = 0;
       node_type_save($type_info);
       node_type_save($type_info);
     }
     }
   }
   }
 }
 }
 
 
 /**
 /**
- * Implements hook_features_enable().
+ * Implements hook_features_enable_feature().
  *
  *
  * When a features module is enabled, modify any node types it provides so
  * When a features module is enabled, modify any node types it provides so
  * they can no longer be deleted manually through the content types UI.
  * they can no longer be deleted manually through the content types UI.
  *
  *
+ * Update the database cache of node types if needed.
+ *
  * @param $module
  * @param $module
  *   Name of module that has been enabled.
  *   Name of module that has been enabled.
  */
  */
-function node_features_enable($module) {
+function node_features_enable_feature($module) {
   if ($default_types = features_get_default('node', $module)) {
   if ($default_types = features_get_default('node', $module)) {
+    $rebuild = FALSE;
     foreach ($default_types as $type_name => $type_info) {
     foreach ($default_types as $type_name => $type_info) {
       // Ensure the type exists.
       // Ensure the type exists.
       if ($type_info = node_type_load($type_name)) {
       if ($type_info = node_type_load($type_name)) {
@@ -156,8 +160,15 @@ function node_features_enable($module) {
         $type_info->custom = 0;
         $type_info->custom = 0;
         $type_info->modified = 0;
         $type_info->modified = 0;
         $type_info->locked = 1;
         $type_info->locked = 1;
+        $type_info->disabled = 0;
         node_type_save($type_info);
         node_type_save($type_info);
       }
       }
+      else {
+        $rebuild = TRUE;
+      }
+    }
+    if ($rebuild) {
+      node_types_rebuild();
     }
     }
   }
   }
 }
 }

+ 41 - 1
sites/all/modules/contrib/admin/features/tests/features.test

@@ -218,7 +218,7 @@ class FeaturesEnableTestCase extends DrupalWebTestCase {
 
 
 
 
 /**
 /**
- * Tests intergration of ctools for features.
+ * Tests integration of ctools for features.
  */
  */
 class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
 class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
   protected $profile = 'testing';
   protected $profile = 'testing';
@@ -287,3 +287,43 @@ class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
     }
     }
   }
   }
 }
 }
+
+
+/**
+ * Test detecting modules as features. 
+ */
+class FeaturesDetectionTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  /**
+   * Test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Feature Detection tests'),
+      'description' => t('Run tests for detecting items as features.') ,
+      'group' => t('Features'),
+    );
+  }
+
+  /**
+   * Set up test.
+   */
+  public function setUp() {
+    parent::setUp(array(
+      'features',
+    ));
+  }
+
+  /**
+   * Run test.
+   */
+  public function test() {
+    module_load_include('inc', 'features', 'features.export');
+    // First test that features_populate inserts the features api key.
+    $export = features_populate(array(), array(), 'features_test_empty_fake');
+    $this->assertTrue(!empty($export['features']['features_api']) && key($export['features']['features_api']) == 'api:' . FEATURES_API, 'Features API key added to new export.');
+    $this->assertTrue((bool)features_get_features('features_test'), 'Features test recognized as a feature.');
+    $this->assertFalse((bool)features_get_features('features'), 'Features module not recognized as a feature.');
+  }
+}

+ 0 - 8
sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc

@@ -29,16 +29,8 @@ function features_test_image_default_styles() {
 
 
   // Exported image style: features_test.
   // Exported image style: features_test.
   $styles['features_test'] = array(
   $styles['features_test'] = array(
-    'name' => 'features_test',
     'effects' => array(
     'effects' => array(
       2 => array(
       2 => array(
-        'label' => 'Scale',
-        'help' => 'Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.',
-        'effect callback' => 'image_scale_effect',
-        'dimensions callback' => 'image_scale_dimensions',
-        'form callback' => 'image_scale_form',
-        'summary theme' => 'image_scale_summary',
-        'module' => 'image',
         'name' => 'image_scale',
         'name' => 'image_scale',
         'data' => array(
         'data' => array(
           'width' => 100,
           'width' => 100,

+ 3 - 3
sites/all/modules/contrib/admin/features/tests/features_test/features_test.info

@@ -21,9 +21,9 @@ features[user_permission][] = create features_test content
 features[views_view][] = features_test
 features[views_view][] = features_test
 hidden = 1
 hidden = 1
 
 
-; Information added by Drupal.org packaging script on 2015-01-05
-version = "7.x-2.3"
+; Information added by Drupal.org packaging script on 2016-04-18
+version = "7.x-2.10"
 core = "7.x"
 core = "7.x"
 project = "features"
 project = "features"
-datestamp = "1420492083"
+datestamp = "1461011641"
 
 

+ 3 - 3
sites/all/modules/contrib/admin/features_extra/fe_block.info

@@ -13,9 +13,9 @@ test_dependencies[] = blockcache_alter
 files[] = tests/features_extra_test_case.test
 files[] = tests/features_extra_test_case.test
 files[] = tests/fe_block.test
 files[] = tests/fe_block.test
 
 
-; Information added by Drupal.org packaging script on 2015-02-06
-version = "7.x-1.0-beta1+4-dev"
+; Information added by Drupal.org packaging script on 2015-08-12
+version = "7.x-1.0"
 core = "7.x"
 core = "7.x"
 project = "features_extra"
 project = "features_extra"
-datestamp = "1423208167"
+datestamp = "1439376840"
 
 

+ 14 - 0
sites/all/modules/contrib/admin/features_extra/fe_date.info

@@ -0,0 +1,14 @@
+name = FE Date
+description = Build date format as features.
+core = 7.x
+package = Features extra
+
+dependencies[] = ctools
+dependencies[] = features
+
+; Information added by Drupal.org packaging script on 2015-08-12
+version = "7.x-1.0"
+core = "7.x"
+project = "features_extra"
+datestamp = "1439376840"
+

+ 345 - 0
sites/all/modules/contrib/admin/features_extra/fe_date.module

@@ -0,0 +1,345 @@
+<?php
+/**
+ * @file
+ * Features export for date components.
+ *
+ *  - 'date_format_types': Hooks 'date_format_types_features_revert()' and
+ *    'date_format_types_features_rebuild()' should be managed by
+ *    'hook_date_format_types()'.
+ *  - 'locale_date_format': Localized date formats.
+ *  - 'custom_date_formats': These formats can be deleted via gui and can be
+ *    used in all date format types.
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function fe_date_features_api() {
+  $components['fe_date_date_format_types'] = array(
+    'name' => t('Date format types'),
+    'feature_source' => TRUE,
+    'default_hook' => 'date_format_types',
+    'default_file' => FEATURES_DEFAULTS_INCLUDED_COMMON,
+  );
+
+  $components['fe_date_custom_date_formats'] = array(
+    'name' => t('Custom date formats'),
+    'feature_source' => TRUE,
+    'default_hook' => 'fe_date_custom_date_formats',
+    'default_file' => FEATURES_DEFAULTS_INCLUDED_COMMON,
+  );
+
+  if (module_exists('locale')) {
+    $components['fe_date_locale_date_format'] = array(
+      'name' => t('Locale date format'),
+      'feature_source' => TRUE,
+      'default_hook' => 'fe_date_locale_date_format',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED_COMMON,
+    );
+  }
+
+  return $components;
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function fe_date_date_format_types_features_export_options() {
+  $options = array();
+  $format_types = system_get_date_types();
+  foreach ($format_types as $type => $format_type) {
+    $options[$type] = $format_type['title'];
+  }
+
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function fe_date_date_format_types_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['fe_date'] = 'fe_date';
+  $pipe = array('variable' => array());
+  $map_variable = features_get_default_map('variable');
+  $map_custom_formats = features_get_default_map('fe_date_custom_date_formats');
+  $map_date_format_types = features_get_default_map('fe_date_date_format_types');
+  foreach ($data as $type) {
+    // Only add format type to export, if it is available.
+    if (system_get_date_types($type)) {
+      $variable = 'date_format_' . $type;
+
+      // Add a dependency when the variable already in a feature but not by use.
+      if (isset($map_variable[$variable]) && $map_variable[$variable] != $module_name) {
+        $export['dependencies'][$map_variable[$variable]] = $map_variable[$variable];
+      }
+      else {
+        // Add strongarm variable for given default date format for this type.
+        $pipe['variable'][$variable] = $variable;
+      }
+
+      $default_format = _fe_date_get_default_format($type);
+
+      // Add a dependency when the default date format is a custom date format
+      // and already in a feature but not by use.
+      if (isset($map_custom_formats[$default_format]) && $map_custom_formats[$default_format] != $module_name) {
+        $export['dependencies'][$map_custom_formats[$default_format]] = $map_custom_formats[$default_format];
+      }
+      else {
+        // Add custom date format for this type.
+        $pipe['fe_date_custom_date_formats'][$default_format] = $default_format;
+      }
+
+      // Add a dependency when the date_format_type already in a feature but
+      // not by use.
+      if (isset($map_date_format_types[$type]) && $map_date_format_types[$type] != $module_name) {
+        $export['dependencies'][$map_date_format_types[$type]] = $map_date_format_types[$type];
+      }
+      else {
+        $export['features']['fe_date_date_format_types'][$type] = $type;
+      }
+
+    }
+  }
+
+  return $pipe;
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function fe_date_date_format_types_features_export_render($module, $data, $export = NULL) {
+  $code = array();
+  $code[] = '  $format_types = array();';
+
+  foreach ($data as $type) {
+    if ($spec = system_get_date_types($type)) {
+      $format_export = features_var_export($spec['title'], '  ');
+      $format_identifier = features_var_export($type);
+      $code[] = "  // Exported date format type: $type";
+      $code[] = "  \$format_types[{$format_identifier}] = {$format_export};";
+    }
+  }
+  $code[] = '  return $format_types;';
+  $code = implode("\n", $code);
+
+  return array('date_format_types' => $code);
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function fe_date_locale_date_format_features_export_options() {
+  $options = array();
+  $format_types = system_get_date_types();
+  $languages = locale_language_list('native');
+
+  foreach ($format_types as $type => $format_type) {
+    foreach ($languages as $langcode => $language) {
+      $format = _fe_date_get_locale_date_format($type, $langcode);
+      if (!empty($format)) {
+        $options["$type::$langcode"] = "{$format_type['title']} ($language)";
+      }
+    }
+  }
+
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function fe_date_locale_date_format_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['fe_date'] = 'fe_date';
+  $map_language = features_get_default_map('language');
+  $map_date_format_types = features_get_default_map('fe_date_date_format_types');
+  $map_locale_date_format = features_get_default_map('fe_date_locale_date_format');
+  $pipe = array('fe_date_date_format_types' => array(), 'language' => array());
+  foreach ($data as $name) {
+    list($type, $langcode) = explode('::', $name . '::', 3);
+    if ($format = _fe_date_get_locale_date_format($type, $langcode)) {
+      // Add a dependency when the type already in a feature but not by use
+      // or system.
+      if (isset($map_date_format_types[$type]) && !in_array($map_date_format_types[$type], array('system', $module_name))) {
+        $export['dependencies'][$map_date_format_types[$type]] = $map_date_format_types[$type];
+      }
+      else {
+        // Add date_format_type to pipe.
+        $pipe['fe_date_date_format_types'][$type] = $type;
+      }
+      // Add a dependency when the language is in a feature but not by use.
+      if (isset($map_language[$langcode]) && $map_language[$langcode] != $module_name) {
+        $export['dependencies'][$map_language[$langcode]] = $map_language[$langcode];
+      }
+      else {
+        // Add language to pipe.
+        $pipe['language'][$langcode] = $langcode;
+      }
+
+      if (isset($map_locale_date_format[$name]) && $map_locale_date_format[$name] != $module_name) {
+        $export['dependencies'][$map_locale_date_format[$name]] = $map_locale_date_format[$name];
+      }
+      else {
+        // Add format to exports.
+        $export['features']['fe_date_locale_date_format'][$name] = $name;
+      }
+    }
+  }
+
+  return $pipe;
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function fe_date_locale_date_format_features_export_render($module, $data, $export = NULL) {
+  $code = array();
+  $code[] = '  $locale_date_formats = array();';
+  $code[] = '';
+  foreach ($data as $name) {
+    list($type, $langcode) = explode('::', $name . '::', 3);
+    if ($format = _fe_date_get_locale_date_format($type, $langcode)) {
+      $var = array(
+        'type' => $type,
+        'format' => $format,
+        'locales' => $langcode ? array($langcode) : array(),
+      );
+      $format_export = features_var_export($var, '  ');
+      $format_identifier = features_var_export($name);
+      $code[] = "  // Exported format: $name";
+      $code[] = "  \$locale_date_formats[{$format_identifier}] = {$format_export};";
+    }
+  }
+
+  $code[] = '  return $locale_date_formats;';
+  $code = implode("\n", $code);
+
+  return array('fe_date_locale_date_format' => $code);
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function fe_date_locale_date_format_features_revert($module) {
+  fe_date_locale_date_format_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ *
+ * hook_date_formats() is used to define the necessary formats, but the
+ * association to the date format type is not set by using that hook. So we have
+ * to set it in the database by ourselves.
+ *
+ * @see hook_date_formats()
+ */
+function fe_date_locale_date_format_features_rebuild($module) {
+  if ($defaults = features_get_default('fe_date_locale_date_format', $module)) {
+    foreach ($defaults as $spec) {
+      foreach ($spec['locales'] as $locale) {
+        locale_date_format_save($locale, $spec['type'], $spec['format']);
+      }
+    }
+  }
+}
+
+/**
+ * Helper function to get the pure format (locale or from variable).
+ */
+function _fe_date_get_locale_date_format($type, $langcode) {
+  // Try to get a locale date format.
+  $format = system_date_format_locale($langcode, $type);
+  // If no format was found, we get the default value.
+  if (empty($format)) {
+    $format = _fe_date_get_default_format($type);
+  }
+  return $format ? $format : NULL;
+}
+
+/**
+ * Helper function to get the pure format (from variable).
+ */
+function _fe_date_get_default_format($type) {
+  // Do not use variable_get() as this may fetch an already localized variable.
+  $format = db_query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'date_format_' . $type))->fetchField();
+
+  return $format !== FALSE ? unserialize($format) : NULL;
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function fe_date_custom_date_formats_features_export_options() {
+  $formats = system_get_date_formats('custom');
+  $options = array();
+  if (!empty($formats)) {
+    foreach (array_keys($formats) as $format) {
+      $options[$format] = $format;
+    }
+  }
+
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function fe_date_custom_date_formats_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['fe_date'] = 'fe_date';
+  foreach ($data as $format) {
+    $map = features_get_default_map('fe_date_custom_date_formats');
+    if (!empty($map[$format]) && $map[$format] != $module_name) {
+      $export['dependencies'][$map[$format]] = $map[$format];
+    }
+    else {
+      $export['features']['fe_date_custom_date_formats'][$format] = $format;
+    }
+  }
+
+  return array();
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function fe_date_custom_date_formats_features_export_render($module, $data, $export = NULL) {
+  $formats = system_get_date_formats('custom');
+  $code = array();
+  $code[] = '  $custom_date_formats = array();';
+  foreach ($data as $format) {
+    if (!empty($formats[$format])) {
+      $format = features_var_export($format);
+      $code[] = "  \$custom_date_formats[{$format}] = {$format};";
+    }
+  }
+  $code[] = '  return $custom_date_formats;';
+  $code = implode("\n", $code);
+
+  return array('fe_date_custom_date_formats' => $code);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function fe_date_custom_date_formats_features_rebuild($module) {
+  fe_date_custom_date_formats_features_revert($module);
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function fe_date_custom_date_formats_features_revert($module) {
+  if ($defaults = features_get_default('fe_date_custom_date_formats', $module)) {
+    $formats = system_get_date_formats('custom');
+    foreach ($defaults as $format) {
+      if (empty($formats[$format])) {
+        $format_data['format'] = trim($format);
+        $format_data['type'] = 'custom';
+        $format_data['locked'] = 0;
+        $format_data['is_new'] = 1;
+        system_date_format_save($format_data);
+      }
+    }
+  }
+}

+ 3 - 3
sites/all/modules/contrib/admin/features_extra/fe_nodequeue.info

@@ -10,9 +10,9 @@ dependencies[] = nodequeue
 files[] = tests/features_extra_test_case.test
 files[] = tests/features_extra_test_case.test
 files[] = tests/fe_nodequeue.test
 files[] = tests/fe_nodequeue.test
 
 
-; Information added by Drupal.org packaging script on 2015-02-06
-version = "7.x-1.0-beta1+4-dev"
+; Information added by Drupal.org packaging script on 2015-08-12
+version = "7.x-1.0"
 core = "7.x"
 core = "7.x"
 project = "features_extra"
 project = "features_extra"
-datestamp = "1423208167"
+datestamp = "1439376840"
 
 

+ 3 - 3
sites/all/modules/contrib/admin/features_extra/fe_profile.info

@@ -7,9 +7,9 @@ dependencies[] = ctools
 dependencies[] = features
 dependencies[] = features
 dependencies[] = profile
 dependencies[] = profile
 
 
-; Information added by Drupal.org packaging script on 2015-02-06
-version = "7.x-1.0-beta1+4-dev"
+; Information added by Drupal.org packaging script on 2015-08-12
+version = "7.x-1.0"
 core = "7.x"
 core = "7.x"
 project = "features_extra"
 project = "features_extra"
-datestamp = "1423208167"
+datestamp = "1439376840"
 
 

+ 3 - 3
sites/all/modules/contrib/admin/features_extra/tests/features_extra_test.info

@@ -14,9 +14,9 @@ features[fe_block_settings][] = block-features_extra_test_block
 features[fe_nodequeue][] = features_extra_test_nodequeue
 features[fe_nodequeue][] = features_extra_test_nodequeue
 features[features_api][] = api:1
 features[features_api][] = api:1
 
 
-; Information added by Drupal.org packaging script on 2015-02-06
-version = "7.x-1.0-beta1+4-dev"
+; Information added by Drupal.org packaging script on 2015-08-12
+version = "7.x-1.0"
 core = "7.x"
 core = "7.x"
 project = "features_extra"
 project = "features_extra"
-datestamp = "1423208167"
+datestamp = "1439376840"
 
 

+ 0 - 0
sites/all/modules/contrib/admin/field_permissions/CHANGELOG.txt


+ 0 - 0
sites/all/modules/contrib/admin/field_permissions/LICENSE.txt


+ 0 - 0
sites/all/modules/contrib/admin/field_permissions/README.txt


+ 10 - 12
sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.css

@@ -1,4 +1,3 @@
-
 /* Table cells. */
 /* Table cells. */
 .field-permissions-cell,
 .field-permissions-cell,
 .field-permissions-header {
 .field-permissions-header {
@@ -29,20 +28,19 @@ html.js .field-permissions-tooltip {
   color: #444;
   color: #444;
   background-color: #f0f0f0;
   background-color: #f0f0f0;
   border: 1px solid #aaa;
   border: 1px solid #aaa;
-
   /* CSS3 properties not supported by all browsers. */
   /* CSS3 properties not supported by all browsers. */
   -webkit-box-shadow: 2px 2px 2px #ccc;
   -webkit-box-shadow: 2px 2px 2px #ccc;
-   -khtml-box-shadow: 2px 2px 2px #ccc;
-    -icab-box-shadow: 2px 2px 2px #ccc;
-     -moz-box-shadow: 2px 2px 2px #ccc;
-       -o-box-shadow: 2px 2px 2px #ccc;
-          box-shadow: 2px 2px 2px #ccc;
+  -khtml-box-shadow: 2px 2px 2px #ccc;
+  -icab-box-shadow: 2px 2px 2px #ccc;
+  -moz-box-shadow: 2px 2px 2px #ccc;
+  -o-box-shadow: 2px 2px 2px #ccc;
+  box-shadow: 2px 2px 2px #ccc;
   -webkit-border-radius: 5px;
   -webkit-border-radius: 5px;
-   -khtml-border-radius: 5px;
-    -icab-border-radius: 5px;
-     -moz-border-radius: 5px;
-       -o-border-radius: 5px;
-          border-radius: 5px;
+  -khtml-border-radius: 5px;
+  -icab-border-radius: 5px;
+  -moz-border-radius: 5px;
+  -o-border-radius: 5px;
+  border-radius: 5px;
 }
 }
 .field-permissions-tooltip .item-list,
 .field-permissions-tooltip .item-list,
 #field-permissions-tooltip .item-list {
 #field-permissions-tooltip .item-list {

+ 81 - 50
sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.inc

@@ -8,13 +8,13 @@
 /**
 /**
  * Obtain the list of field permissions.
  * Obtain the list of field permissions.
  *
  *
- * @param $field_label
+ * @param string $field_label
  *   The human readable name of the field to use when constructing permission
  *   The human readable name of the field to use when constructing permission
  *   names. Usually this will be derived from one or more of the field instance
  *   names. Usually this will be derived from one or more of the field instance
  *   labels.
  *   labels.
  */
  */
 function field_permissions_list($field_label = '') {
 function field_permissions_list($field_label = '') {
-  return array(
+  $permissions = array(
     'create' => array(
     'create' => array(
       'label' => t('Create field'),
       'label' => t('Create field'),
       'title' => t('Create own value for field %field', array('%field' => $field_label)),
       'title' => t('Create own value for field %field', array('%field' => $field_label)),
@@ -36,46 +36,47 @@ function field_permissions_list($field_label = '') {
       'title' => t("View anyone's value for field %field", array('%field' => $field_label)),
       'title' => t("View anyone's value for field %field", array('%field' => $field_label)),
     ),
     ),
   );
   );
+
+  drupal_alter('field_permissions_list', $permissions, $field_label);
+
+  return $permissions;
 }
 }
 
 
 /**
 /**
  * Returns field permissions in a format suitable for use in hook_permission().
  * Returns field permissions in a format suitable for use in hook_permission().
  *
  *
- * @param $field
+ * @param array $field
  *   The field to return permissions for.
  *   The field to return permissions for.
- * @param $label
+ * @param mixed $label
  *   (optional) The human readable name of the field to use when constructing
  *   (optional) The human readable name of the field to use when constructing
  *   permission names; for example, this might be the label of one of the
  *   permission names; for example, this might be the label of one of the
  *   corresponding field instances. If not provided, an appropriate label will
  *   corresponding field instances. If not provided, an appropriate label will
  *   be automatically derived from all the field's instances.
  *   be automatically derived from all the field's instances.
  *
  *
- * @return
+ * @return array
  *   An array of permission information, suitable for use in hook_permission().
  *   An array of permission information, suitable for use in hook_permission().
  */
  */
 function field_permissions_list_field_permissions($field, $label = NULL) {
 function field_permissions_list_field_permissions($field, $label = NULL) {
-  $description = '';
-
-  // If there is no preferred label, construct one from all the instance
-  // labels.
   if (!isset($label)) {
   if (!isset($label)) {
-    $labels = array();
-    foreach ($field['bundles'] as $entity_type => $bundles) {
-      foreach ($bundles as $bundle_name) {
-        $instance = field_info_instance($entity_type, $field['field_name'], $bundle_name);
-        $labels[] = $instance['label'];
+    $label = $field['field_name'];
+  }
+  $instances = array();
+  $description = '';
+  foreach ($field['bundles'] as $entity_type => $bundles) {
+    foreach ($bundles as $bundle_name) {
+      $instance = field_info_instance($entity_type, $field['field_name'], $bundle_name);
+      $entity = entity_get_info($entity_type);
+      if (!isset($entity['bundles'][$bundle_name])) {
+        continue;
       }
       }
+      $instance_desc_tokens = array(
+        $entity['label'],
+        $entity['bundles'][$bundle_name]['label'],
+        $instance['label'],
+      );
+      $instances[] = '"' . implode(':', $instance_desc_tokens) . '"';
     }
     }
-    // If all the instances have the same label, just use that. Otherwise, use
-    // the field name (with the full list of instance labels as the permission
-    // description).
-    $labels = array_unique($labels);
-    if (count($labels) == 1) {
-      $label = array_shift($labels);
-    }
-    else {
-      $label = $field['field_name'];
-      $description = t('This field appears as: %instances', array('%instances' => implode(', ', $labels)));
-    }
+    $description = t('This field appears as: %instances.', array('%instances' => implode(', ', $instances)));
   }
   }
 
 
   $permissions = array();
   $permissions = array();
@@ -91,7 +92,7 @@ function field_permissions_list_field_permissions($field, $label = NULL) {
 }
 }
 
 
 /**
 /**
- * Implementation of hook_permission().
+ * Implements hook_permission().
  */
  */
 function _field_permissions_permission() {
 function _field_permissions_permission() {
   $perms = array(
   $perms = array(
@@ -145,9 +146,7 @@ function _field_permissions_field_settings_form_alter(&$form, $form_state, $form
     '#type' => 'container',
     '#type' => 'container',
     '#states' => array(
     '#states' => array(
       'visible' => array(
       'visible' => array(
-        // We must cast this to a string until http://drupal.org/node/879580 is
-        // fixed.
-        ':input[name="field[field_permissions][type]"]' => array('value' => (string) FIELD_PERMISSIONS_CUSTOM),
+        ':input[name="field[field_permissions][type]"]' => array('value' => FIELD_PERMISSIONS_CUSTOM),
       ),
       ),
     ),
     ),
     // Custom styling for the permissions matrix on the field settings page.
     // Custom styling for the permissions matrix on the field settings page.
@@ -156,6 +155,23 @@ function _field_permissions_field_settings_form_alter(&$form, $form_state, $form
     ),
     ),
   );
   );
 
 
+  $form['field']['field_permissions']['permission_warning'] = array(
+    '#type' => 'item',
+    '#markup' => '<div class="messages error"><b>'
+    . t('Field permissions error')
+    . '</b><br />'
+    . t('If you are seeing this message and are not seeing a table of permissions, something is wrong! See !link for more information.', array(
+      '!link' => l(t('the Field Permission documentation'), 'https://www.drupal.org/node/2802067#known-issues', array(
+        'attributes' => array('target' => '_blank'),
+      )),
+    )) . '</div>',
+    '#states' => array(
+      'visible' => array(
+        ':input[name="field[field_permissions][type]"]' => array('value' => FIELD_PERMISSIONS_CUSTOM),
+      ),
+    ),
+  );
+
   // Add the field permissions matrix itself. Wait until the #pre_render stage
   // Add the field permissions matrix itself. Wait until the #pre_render stage
   // to move it to the above container, to avoid having the permissions data
   // to move it to the above container, to avoid having the permissions data
   // saved as part of the field record.
   // saved as part of the field record.
@@ -183,15 +199,15 @@ function _field_permissions_field_settings_form_alter(&$form, $form_state, $form
  * to actually be saved. For an example submit handler, see
  * to actually be saved. For an example submit handler, see
  * _field_permissions_field_settings_form_submit().
  * _field_permissions_field_settings_form_submit().
  *
  *
- * @param $field
+ * @param array $field
  *   The field whose permissions will be displayed in the matrix.
  *   The field whose permissions will be displayed in the matrix.
- * @param $instance
+ * @param array $instance
  *   The field instance for which the permissions will be displayed. Although
  *   The field instance for which the permissions will be displayed. Although
  *   the permissions are per-field rather than per-instance, the instance label
  *   the permissions are per-field rather than per-instance, the instance label
  *   will be used to display an appropriate human-readable name for each
  *   will be used to display an appropriate human-readable name for each
  *   permission.
  *   permission.
  *
  *
- * @return
+ * @return array
  *   A form array defining the permissions matrix.
  *   A form array defining the permissions matrix.
  *
  *
  * @see user_admin_permissions()
  * @see user_admin_permissions()
@@ -327,37 +343,52 @@ function _field_permissions_field_settings_form_submit($form, &$form_state) {
  * Menu callback; Field permissions overview.
  * Menu callback; Field permissions overview.
  */
  */
 function field_permissions_overview() {
 function field_permissions_overview() {
-  drupal_add_css(drupal_get_path('module', 'field_permissions') .'/field_permissions.admin.css');
+  drupal_add_css(drupal_get_path('module', 'field_permissions') . '/field_permissions.admin.css');
 
 
-  $headers = array(t('Field name'), t('Field type'), t('Entity type'), t('Used in'));
+  $headers = array(
+    t('Field name'),
+    t('Field type'),
+    t('Entity type'),
+    t('Used in'),
+  );
   foreach (field_permissions_list() as $permission_type => $permission_info) {
   foreach (field_permissions_list() as $permission_type => $permission_info) {
     $headers[] = array('data' => $permission_info['label'], 'class' => 'field-permissions-header');
     $headers[] = array('data' => $permission_info['label'], 'class' => 'field-permissions-header');
   }
   }
   $destination = drupal_get_destination();
   $destination = drupal_get_destination();
 
 
-  // Load list of field instances, types and bundles in the system.
-  $instances = field_info_instances();
+  // Load list of fields, field types and bundles in the system.
   $field_types = field_info_field_types();
   $field_types = field_info_field_types();
-  $bundles = field_info_bundles();
+  $bundles_info = field_info_bundles();
 
 
   // Retrieve the permissions for each role.
   // Retrieve the permissions for each role.
   $role_permissions = user_role_permissions(user_roles());
   $role_permissions = user_role_permissions(user_roles());
 
 
   // Based on field_ui_fields_list() in field_ui.admin.inc.
   // Based on field_ui_fields_list() in field_ui.admin.inc.
   $rows = array();
   $rows = array();
-  foreach ($instances as $obj_type => $type_bundles) {
-    foreach ($type_bundles as $bundle => $bundle_instances) {
-      foreach ($bundle_instances as $field_name => $instance) {
+  foreach (field_info_fields() as $field_name => $field) {
+    foreach ($field['bundles'] as $entity_type => $bundles) {
+      foreach ($bundles as $bundle) {
+        // Some fields might belong to bundles that are disabled (which are not
+        // returned by field_info_bundles()).
+        // @see https://www.drupal.org/node/1351506
+        if (!isset($bundles_info[$entity_type][$bundle])) {
+          continue;
+        }
         // Each field will have a row in the table.
         // Each field will have a row in the table.
-        $field = field_info_field($field_name);
-        $admin_path = _field_ui_bundle_admin_path($obj_type, $bundle);
+        if (module_exists('field_ui')) {
+          $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
+          $field_admin_path = l($bundles_info[$entity_type][$bundle]['label'], $admin_path . '/fields/' . $field_name, array(
+            'query' => $destination,
+            'fragment' => 'edit-field-field-permissions-type',
+          ));
+        }
+        else {
+          $field_admin_path = $bundles_info[$entity_type][$bundle]['label'];
+        }
         $rows[$field_name]['data'][0] = $field['locked'] ? t('@field_name (Locked)', array('@field_name' => $field_name)) : $field_name;
         $rows[$field_name]['data'][0] = $field['locked'] ? t('@field_name (Locked)', array('@field_name' => $field_name)) : $field_name;
-        $rows[$field_name]['data'][1] = t($field_types[$field['type']]['label']);
-        $rows[$field_name]['data'][2] = $obj_type;
-        $rows[$field_name]['data'][3][] = l($bundles[$obj_type][$bundle]['label'], $admin_path . '/fields/'. $field_name, array(
-          'query' => $destination,
-          'fragment' => 'edit-field-field-permissions-type',
-        ));
+        $rows[$field_name]['data'][1] = $field_types[$field['type']]['label'];
+        $rows[$field_name]['data'][2] = $entity_type;
+        $rows[$field_name]['data'][3][] = $field_admin_path;
         $rows[$field_name]['class'] = $field['locked'] ? array('menu-disabled') : array('');
         $rows[$field_name]['class'] = $field['locked'] ? array('menu-disabled') : array('');
 
 
         // Append field permissions information to the report.
         // Append field permissions information to the report.
@@ -381,7 +412,7 @@ function field_permissions_overview() {
             $all_users_have_access = isset($role_permissions[DRUPAL_ANONYMOUS_RID][$permission]) && isset($role_permissions[DRUPAL_AUTHENTICATED_RID][$permission]);
             $all_users_have_access = isset($role_permissions[DRUPAL_ANONYMOUS_RID][$permission]) && isset($role_permissions[DRUPAL_AUTHENTICATED_RID][$permission]);
             $status_class = $all_users_have_access ? 'field-permissions-status-on' : 'field-permissions-status-off';
             $status_class = $all_users_have_access ? 'field-permissions-status-on' : 'field-permissions-status-off';
             $title = $all_users_have_access ? t('All users have this permission') : t('Not all users have this permission');
             $title = $all_users_have_access ? t('All users have this permission') : t('Not all users have this permission');
-            $data = l('', 'admin/people/permissions', array(
+            $data = l(NULL, 'admin/people/permissions', array(
               'attributes' => array(
               'attributes' => array(
                 'class' => array('field-permissions-status', $status_class),
                 'class' => array('field-permissions-status', $status_class),
                 'title' => $title,
                 'title' => $title,
@@ -416,7 +447,7 @@ function field_permissions_overview() {
 
 
     // Allow external modules alter the table headers and rows.
     // Allow external modules alter the table headers and rows.
     foreach (module_implements('field_permissions_overview_alter') as $module) {
     foreach (module_implements('field_permissions_overview_alter') as $module) {
-      $function = $module .'_field_permissions_overview_alter';
+      $function = $module . '_field_permissions_overview_alter';
       $function($headers, $rows);
       $function($headers, $rows);
     }
     }
 
 

+ 59 - 48
sites/all/modules/contrib/admin/field_permissions/field_permissions.admin.js

@@ -1,63 +1,74 @@
+/**
+ * @file
+ */
+
 (function ($) {
 (function ($) {
 
 
-Drupal.behaviors.fieldPermissionsSettings = {
-  attach: function (context) {
-    // For user fields, we want the "Create own value for field X" permission
-    // row to only be displayed when it's meaningful (i.e., when the "Display
-    // on user registration form" checkbox is checked).
-    var $user_register_form_checkbox, $required_field_checkbox, $create_permission_row;
-    $user_register_form_checkbox = $('.form-item-instance-settings-user-register-form .form-checkbox', context);
-    if ($user_register_form_checkbox.length) {
-      // The "Required field" checkbox can cause the user registration checkbox
-      // to change, so we need it also.
-      $required_field_checkbox = $('.form-item-instance-required .form-checkbox', context);
-      if ($required_field_checkbox.length) {
-        // Get the permissions table row corresponding to the "Create own value
-        // for field X" permission. The theme_user_admin_permissions() function
-        // does not give us a good way to directly detect which row contains
-        // the create permissions, so we have rely on the fact that it will be
-        // the first row.
-        $create_permission_row = $('table#permissions tbody tr', context).filter(':first');
-        new Drupal.fieldPermissions.HideCreatePermission($user_register_form_checkbox, $required_field_checkbox, $create_permission_row);
+  Drupal.behaviors.fieldPermissionsSettings = {
+    attach: function (context) {
+      // For user fields, we want the "Create own value for field X" permission
+      // row to only be displayed when it's meaningful (i.e., when the "Display
+      // on user registration form" checkbox is checked).
+      var $user_register_form_checkbox, $required_field_checkbox, $create_permission_row;
+      $user_register_form_checkbox = $('.form-item-instance-settings-user-register-form .form-checkbox', context);
+      if ($user_register_form_checkbox.length) {
+        // The "Required field" checkbox can cause the user registration checkbox
+        // to change, so we need it also.
+        $required_field_checkbox = $('.form-item-instance-required .form-checkbox', context);
+        if ($required_field_checkbox.length) {
+          // Get the permissions table row corresponding to the "Create own value
+          // for field X" permission. The theme_user_admin_permissions() function
+          // does not give us a good way to directly detect which row contains
+          // the create permissions, so we have rely on the fact that it will be
+          // the first row.
+          $create_permission_row = $('table#permissions tbody tr', context).filter(':first');
+          new Drupal.fieldPermissions.HideCreatePermission($user_register_form_checkbox, $required_field_checkbox, $create_permission_row);
+        }
       }
       }
+
+      // Show a warning if there's no available permissions matrix.
+      $('#edit-field-field-permissions-permission-warning').toggle(!$('#permissions').length);
+
+      $('[name="field[field_permissions][type]"]').bind('change', function(option) {
+        $('#edit-field-field-permissions-permission-warning').toggle(!$('#permissions').length);
+      });
     }
     }
-  }
-};
+  };
 
 
-Drupal.fieldPermissions = {};
+  Drupal.fieldPermissions = {};
 
 
-/**
+  /**
  * Constructor for the HideCreatePermission object.
  * Constructor for the HideCreatePermission object.
  *
  *
  * This object hides and shows the "Create own value for field X" permission
  * This object hides and shows the "Create own value for field X" permission
  * for user fields when it is appropriate to do so, depending on the state of
  * for user fields when it is appropriate to do so, depending on the state of
  * the "Display on user registration form" and "Required field" checkboxes.
  * the "Display on user registration form" and "Required field" checkboxes.
  */
  */
-Drupal.fieldPermissions.HideCreatePermission = function ($user_register_form_checkbox, $required_field_checkbox, $create_permission_row) {
-  this.$user_register_form_checkbox = $user_register_form_checkbox;
-  this.$create_permission_row = $create_permission_row;
-  // Start off by making sure the create permission row has the correct
-  // visibility.
-  this.setCreatePermissionVisibility();
-  // Set the row's visibility again whenever the user registration checkbox
-  // changes, or when the required field checkbox (which controls it) changes.
-  $user_register_form_checkbox.bind('change', $.proxy(this.setCreatePermissionVisibility, this));
-  $required_field_checkbox.bind('change', $.proxy(this.setCreatePermissionVisibility, this));
-};
+  Drupal.fieldPermissions.HideCreatePermission = function ($user_register_form_checkbox, $required_field_checkbox, $create_permission_row) {
+    this.$user_register_form_checkbox = $user_register_form_checkbox;
+    this.$create_permission_row = $create_permission_row;
+    // Start off by making sure the create permission row has the correct
+    // visibility.
+    this.setCreatePermissionVisibility();
+    // Set the row's visibility again whenever the user registration checkbox
+    // changes, or when the required field checkbox (which controls it) changes.
+    $user_register_form_checkbox.bind('change', $.proxy(this.setCreatePermissionVisibility, this));
+    $required_field_checkbox.bind('change', $.proxy(this.setCreatePermissionVisibility, this));
+  };
 
 
-/**
- * Set the correct visibility of the "Create own value for field X" permission.
- */
-Drupal.fieldPermissions.HideCreatePermission.prototype.setCreatePermissionVisibility = function () {
-  // Granting permissions for "Create own value for field X" only makes sense
-  // when the field is configured to appear on the user registration form, so
-  // only show the row in the permissions table then.
-  if (this.$user_register_form_checkbox.is(':checked')) {
-    this.$create_permission_row.show();
-  }
-  else {
-    this.$create_permission_row.hide();
-  }
-};
+  /**
+   * Set the correct visibility of the "Create own value for field X" permission.
+   */
+  Drupal.fieldPermissions.HideCreatePermission.prototype.setCreatePermissionVisibility = function () {
+    // Granting permissions for "Create own value for field X" only makes sense
+    // when the field is configured to appear on the user registration form, so
+    // only show the row in the permissions table then.
+    if (this.$user_register_form_checkbox.is(':checked')) {
+      this.$create_permission_row.show();
+    }
+    else {
+      this.$create_permission_row.hide();
+    }
+  };
 
 
 })(jQuery);
 })(jQuery);

+ 85 - 0
sites/all/modules/contrib/admin/field_permissions/field_permissions.api.php

@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Field Permission module.
+ */
+
+/**
+ * Defines the owner of an entity.
+ *
+ * Because not all entities have uids, this hook allows other modules to specify
+ * one.
+ *
+ * @param int $uid
+ *   The userid that will be checked against the current user's account->uid.
+ * @param object $entity
+ *   The entity this field belongs to.
+ */
+// @codingStandardsIgnoreStart
+function hook_field_permissions_userid_ENTITY_TYPE_alter(&$uid, $entity) {
+  // This example always assigns user 15 as the owner of an entity.
+  $uid = 15;
+}
+// @codingStandardsIgnoreEnd
+
+/**
+ * Alter the permissions handled by field_permissions module.
+ *
+ * @param array $permissions
+ *   The $permissions array created by the Field permissions module.
+ * @param string $field_label
+ *   The field name.
+ */
+function hook_field_permissions_list_alter(&$permissions, $field_label) {
+  $permissions += array(
+    'view own node preview' => array(
+      'label' => t('View own field on node preview'),
+      'title' => t('View own value for field %field on node preview', array('%field' => $field_label)),
+    ),
+    'view node preview' => array(
+      'label' => t('View field on node preview'),
+      'title' => t("View anyone's value for field %field on node preview", array('%field' => $field_label)),
+    ),
+  );
+}
+
+/**
+ * Hook invoked with custom field permissions.
+ *
+ * This hook can be used to revoke access to the field. If access is not
+ * revoked, default access of the Field permissions module will apply.
+ *
+ * @param string $op
+ *   The operation to be performed. Possible values: 'edit', 'view'.
+ * @param array $field
+ *   The field on which the operation is to be performed.
+ * @param string $entity_type
+ *   The type of $entity; for example, 'node' or 'user'.
+ * @param object $entity
+ *   (optional) The entity for the operation.
+ * @param object $account
+ *   (optional) The account to check; if not given use currently logged in user.
+ *
+ * @return bool
+ *   FALSE if the operation is not allowed.
+ *
+ * @see field_permissions_field_access()
+ * @see field_access()
+ */
+function hook_field_permissions_custom_field_access($op, $field, $entity_type, $entity, $account) {
+  if ($op == 'view' && $entity_type == 'node' && !empty($entity)) {
+    // Check if user has access to view this field in any entity.
+    if (!user_access('view node preview ' . $field['field_name'], $account)) {
+      return FALSE;
+    }
+
+    // If the user has permission to view entities that they own, return TRUE if
+    // they own this entity or FALSE if they don't.
+    if (user_access('view own node preview ' . $field['field_name'], $account)) {
+      return _field_permissions_entity_is_owned_by_account($entity, $account);
+    }
+  }
+
+  return TRUE;
+}

+ 3 - 5
sites/all/modules/contrib/admin/field_permissions/field_permissions.info

@@ -2,14 +2,12 @@ name = Field Permissions
 description = Set field-level permissions to create, update or view fields.
 description = Set field-level permissions to create, update or view fields.
 package = Fields
 package = Fields
 core = 7.x
 core = 7.x
-files[] = field_permissions.module
-files[] = field_permissions.admin.inc
 files[] = field_permissions.test
 files[] = field_permissions.test
 configure = admin/reports/fields/permissions
 configure = admin/reports/fields/permissions
 
 
-; Information added by drupal.org packaging script on 2012-01-25
-version = "7.x-1.0-beta2"
+; Information added by Drupal.org packaging script on 2016-10-16
+version = "7.x-1.0"
 core = "7.x"
 core = "7.x"
 project = "field_permissions"
 project = "field_permissions"
-datestamp = "1327510549"
+datestamp = "1476649740"
 
 

+ 22 - 0
sites/all/modules/contrib/admin/field_permissions/field_permissions.install

@@ -16,6 +16,28 @@ function field_permissions_install() {
     ->execute();
     ->execute();
 }
 }
 
 
+/**
+ * Implements hook_uninstall().
+ */
+function field_permissions_uninstall() {
+
+  // Collect all the field data that reference "field_permissions", within
+  // the field_config table.
+  //
+  $records = db_query("SELECT * FROM field_config WHERE data LIKE '%field_permissions%'");
+
+  foreach ($records as $record) {
+    $data = $record->data;
+    $data = unserialize($data);
+    unset($data['field_permissions']);
+    $data = serialize($data);
+
+    // Update the record.
+    db_query("UPDATE field_config SET data = :data WHERE id = :id",
+      array(':data' => $data, ':id' => $record->id));
+  }
+}
+
 /**
 /**
  * Sets a larger weight for the module so that the Field Permissions become available.
  * Sets a larger weight for the module so that the Field Permissions become available.
  */
  */

+ 85 - 1
sites/all/modules/contrib/admin/field_permissions/field_permissions.module

@@ -79,6 +79,13 @@ function field_permissions_form_field_ui_field_edit_form_alter(&$form, &$form_st
   return _field_permissions_field_settings_form_alter($form, $form_state, $form_id);
   return _field_permissions_field_settings_form_alter($form, $form_state, $form_id);
 }
 }
 
 
+/**
+ * Implements hook_field_permissions_userid_ENTITY_TYPE_alter().
+ */
+function field_permissions_field_permissions_userid_field_collection_item_alter(&$uid, $entity) {
+  $uid = isset($entity->hostEntity()->uid) ? $entity->hostEntity()->uid : $uid;
+}
+
 /**
 /**
  * Implementation of hook_field_access().
  * Implementation of hook_field_access().
  *
  *
@@ -124,6 +131,12 @@ function field_permissions_field_access($op, $field, $entity_type, $entity, $acc
   }
   }
   // Otherwise, check access by permission.
   // Otherwise, check access by permission.
   elseif ($field['field_permissions']['type'] == FIELD_PERMISSIONS_CUSTOM) {
   elseif ($field['field_permissions']['type'] == FIELD_PERMISSIONS_CUSTOM) {
+    // Allow other modules to deny access first.
+    $result = module_invoke_all('field_permissions_custom_field_access', $op, $field, $entity_type, $entity, $account);
+    if (in_array(FALSE, $result)) {
+      return FALSE;
+    }
+
     if (!isset($entity)) {
     if (!isset($entity)) {
       return field_permissions_empty_entity_access($op, $field['field_name'], $account);
       return field_permissions_empty_entity_access($op, $field['field_name'], $account);
     }
     }
@@ -258,5 +271,76 @@ function _field_permissions_entity_is_owned_by_account($entity, $account) {
   // set (for example, if the entity type does not store a uid or does not have
   // set (for example, if the entity type does not store a uid or does not have
   // a concept of "ownership"), we need to assume that the provided user
   // a concept of "ownership"), we need to assume that the provided user
   // account does not own it.
   // account does not own it.
-  return isset($entity->uid) && $entity->uid == $account->uid;
+  $uid = isset($entity->uid) ? $entity->uid : FALSE;
+  if (method_exists($entity, 'entityType')) {
+    drupal_alter('field_permissions_userid_' . $entity->entityType(), $uid, $entity);
+  }
+
+  return $uid === $account->uid;
+}
+
+/**
+ * Implements hook_features_pipe_COMPONENT_alter().
+ *
+ * Add field permissions to features when exporting a field_base.
+ */
+function field_permissions_features_pipe_field_base_alter(&$pipe, $data, $export) {
+  // Validate if there are field_base components that will be exported for this
+  // feature.
+  if (isset($export['features']['field_base'])) {
+    module_load_include('inc', 'field_permissions', 'field_permissions.admin');
+    // Iterate through the exported field_base components for this feature and
+    // add the defined field permissions.
+    foreach ($export['features']['field_base'] as $field_name) {
+      $field = field_info_field($field_name);
+      if (isset($field['field_permissions']['type']) && $field['field_permissions']['type'] == FIELD_PERMISSIONS_CUSTOM) {
+        $perms = field_permissions_list_field_permissions($field, '');
+        foreach ($perms as $perm => $info) {
+          $pipe['user_permission'][] = $perm;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_attach_form().
+ */
+function field_permissions_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+  // Some fields are validated if they are #required even if field's #access
+  // property is set to false. For example: file/image fields, options fields.
+  foreach (element_children($form) as $key) {
+    if (isset($form[$key]['#access']) && !$form[$key]['#access']) {
+      _field_permissions_make_elements_non_required($form[$key]);
+    }
+  }
+}
+
+/**
+ * Sets the #required property to FALSE recursively on form elements.
+ */
+function _field_permissions_make_elements_non_required(&$elements) {
+  if (!is_array($elements)) {
+    return;
+  }
+  if (!empty($elements['#required'])) {
+    $elements['#required'] = FALSE;
+  }
+  foreach (element_children($elements) as $key) {
+    _field_permissions_make_elements_non_required($elements[$key]);
+  }
+}
+
+/**
+ * Implements hook_field_delete_field().
+ */
+function field_permissions_field_delete_field($field) {
+  // Delete any permissions related to the deleted field.
+  $all_permissions = array_keys(field_permissions_permission());
+  if (!empty($all_permissions)) {
+    db_delete('role_permission')
+      ->condition('module', 'field_permissions')
+      ->condition('permission', $all_permissions, 'NOT IN')
+      ->execute();
+  }
 }
 }

+ 6 - 6
sites/all/modules/contrib/admin/field_permissions/field_permissions.test

@@ -26,7 +26,7 @@ class FieldPermissionsTestCase extends DrupalWebTestCase {
     parent::setUp('field_ui', 'field_permissions');
     parent::setUp('field_ui', 'field_permissions');
 
 
     // Create test user.
     // Create test user.
-    $admin_permissions = array('access content', 'administer nodes', 'bypass node access', 'administer content types', 'administer taxonomy', 'administer permissions', 'create page content');
+    $admin_permissions = array('access content', 'administer nodes', 'bypass node access', 'administer content types', 'administer taxonomy', 'administer permissions', 'create page content', 'administer fields');
     $this->limited_user = $this->drupalCreateUser($admin_permissions);
     $this->limited_user = $this->drupalCreateUser($admin_permissions);
     $all_rids = array_keys($this->limited_user->roles);
     $all_rids = array_keys($this->limited_user->roles);
     sort($all_rids);
     sort($all_rids);
@@ -162,11 +162,11 @@ class FieldPermissionsTestCase extends DrupalWebTestCase {
     // See if we have that exposed on the permissions UI as well now.
     // See if we have that exposed on the permissions UI as well now.
     $this->drupalGet('admin/people/permissions');
     $this->drupalGet('admin/people/permissions');
     $this->assertText(t('Field Permissions'));
     $this->assertText(t('Field Permissions'));
-    $this->assertRaw(t('Create own value for field %field', array('%field' => $field_info['name'])));
-    $this->assertRaw(t('Edit own value for field %field', array('%field' => $field_info['name'])));
-    $this->assertRaw(t("Edit anyone's value for field %field", array('%field' => $field_info['name'])));
-    $this->assertRaw(t('View own value for field %field', array('%field' => $field_info['name'])));
-    $this->assertRaw(t("View anyone's value for field %field", array('%field' => $field_info['name'])));
+    $this->assertRaw(t('Create own value for field %field', array('%field' => $field_info['machine_name'])));
+    $this->assertRaw(t('Edit own value for field %field', array('%field' => $field_info['machine_name'])));
+    $this->assertRaw(t("Edit anyone's value for field %field", array('%field' => $field_info['machine_name'])));
+    $this->assertRaw(t('View own value for field %field', array('%field' => $field_info['machine_name'])));
+    $this->assertRaw(t("View anyone's value for field %field", array('%field' => $field_info['machine_name'])));
 
 
     // == CREATE ===============================================================
     // == CREATE ===============================================================
 
 

+ 0 - 0
sites/all/modules/contrib/admin/field_permissions/images/field_permissions.status-off.png


+ 0 - 0
sites/all/modules/contrib/admin/field_permissions/images/field_permissions.status-on.png


+ 3 - 3
sites/all/modules/contrib/admin/fpa/fpa.info

@@ -2,9 +2,9 @@ name = "Fast Permissions Administration"
 description = "Fast filtering on permissions administration form."
 description = "Fast filtering on permissions administration form."
 core = 7.x
 core = 7.x
 package = Administration
 package = Administration
-; Information added by Drupal.org packaging script on 2014-10-15
-version = "7.x-2.6+1-dev"
+; Information added by Drupal.org packaging script on 2014-08-22
+version = "7.x-2.6"
 core = "7.x"
 core = "7.x"
 project = "fpa"
 project = "fpa"
-datestamp = "1413408942"
+datestamp = "1408744435"
 
 

+ 1 - 1
sites/all/modules/contrib/admin/fpa/fpa.theme.inc

@@ -519,7 +519,7 @@ function _fpa_wrapper($permissions_table, $modules, $user_roles, $actions_output
     $module_item = $module_template;
     $module_item = $module_template;
     
     
     $module_item[FPA_ATTR_MODULE] = $module[FPA_ATTR_MODULE];
     $module_item[FPA_ATTR_MODULE] = $module[FPA_ATTR_MODULE];
-    $module_item[FPA_ATTR_PERMISSION] = array_reduce(array_pad($module[FPA_ATTR_PERMISSION], 1, array()), 'array_merge', array());
+    $module_item[FPA_ATTR_PERMISSION] = array_reduce($module[FPA_ATTR_PERMISSION], 'array_merge', array());
     
     
     // Use link for accessibility and tabability.
     // Use link for accessibility and tabability.
     $options = array(
     $options = array(

+ 13 - 0
sites/all/modules/contrib/admin/linkit/better-autocomplete/better-autocomplete.css

@@ -24,6 +24,9 @@
   cursor: default;
   cursor: default;
   background: white;
   background: white;
   border-bottom: 1px solid #bfbfbf;
   border-bottom: 1px solid #bfbfbf;
+  z-index: 10;
+  max-height: 330px;
+  box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); /* Visually indicate that results are in the topmost layer */
 }
 }
 
 
 /* Groups */
 /* Groups */
@@ -82,3 +85,13 @@
 .better-autocomplete > .result.highlight > p {
 .better-autocomplete > .result.highlight > p {
   color: white;
   color: white;
 }
 }
+
+/* ARIA */
+
+.better-autocomplete-aria {
+  position: absolute !important;
+  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+  clip: rect(1px, 1px, 1px, 1px);
+  overflow: hidden;
+  height: 1px;
+}

+ 804 - 789
sites/all/modules/contrib/admin/linkit/better-autocomplete/jquery.better-autocomplete.js

@@ -1,4 +1,3 @@
-
 /**
 /**
  * @fileOverview Better Autocomplete is a flexible jQuery plugin which offers
  * @fileOverview Better Autocomplete is a flexible jQuery plugin which offers
  * rich text autocompletion, both from local and remote sources.
  * rich text autocompletion, both from local and remote sources.
@@ -97,847 +96,863 @@
 
 
 (function($) {
 (function($) {
 
 
-$.fn.betterAutocomplete = function(method) {
-
-  /*
-   * Each method expects the "this" object to be a valid DOM text input node.
-   * The methods "enable", "disable" and "destroy" expects an instance of a
-   * BetterAutocomplete object as their first argument.
-   */
-  var methods = {
-    init: function(resource, options, callbacks) {
-      var $input = $(this),
-        bac = new BetterAutocomplete($input, resource, options, callbacks);
-      $input.data('better-autocomplete', bac);
-      bac.enable();
-    },
-    enable: function(bac) {
-      bac.enable();
-    },
-    disable: function(bac) {
-      bac.disable();
-    },
-    destroy: function(bac) {
-      bac.destroy();
-    }
-  }, args = Array.prototype.slice.call(arguments, 1);
-
-  // Method calling logic
-  this.each(function() {
-    switch (method) {
-    case 'init':
-      methods[method].apply(this, args);
-      break;
-    case 'enable':
-    case 'disable':
-    case 'destroy':
-      var bac = $(this).data('better-autocomplete');
-      if (bac instanceof BetterAutocomplete) {
-        methods[method].call(this, bac);
-      }
-      break;
-    default:
-      $.error(['Method', method,
-          'does not exist in jQuery.betterAutocomplete.'].join(' '));
-    }
-  });
-
-  // Maintain chainability
-  return this;
-};
-
-/**
- * The BetterAutocomplete constructor function. Returns a BetterAutocomplete
- * instance object.
- *
- * @private @constructor
- * @name BetterAutocomplete
- *
- * @param {Object} $input
- *   A single input element wrapped in jQuery.
- */
-var BetterAutocomplete = function($input, resource, options, callbacks) {
-
-  var lastRenderedQuery = '',
-    cache = {}, // Key-valued caching of search results
-    cacheOrder = [], // Array of query strings, in the order they are added
-    cacheSize = 0, // Keep count of the cache's size
-    timer, // Used for options.delay
-    activeRemoteCalls = [], // A flat array of query strings that are pending
-    disableMouseHighlight = false, // Suppress the autotriggered mouseover event
-    inputEvents = {},
-    isLocal = ($.type(resource) != 'string'),
-    $results = $('<ul />').addClass('better-autocomplete'),
-    hiddenResults = true, // $results are hidden
-    preventBlurTimer = null; // IE bug workaround, see below in code.
-
-  options = $.extend({
-    charLimit: isLocal ? 1 : 3,
-    delay: 350, // milliseconds
-    caseSensitive: false,
-    cacheLimit: isLocal ? 0 : 256, // Number of result objects
-    remoteTimeout: 10000, // milliseconds
-    crossOrigin: false,
-    selectKeys: [9, 13], // [tab, enter]
-    autoHighlight: true // Automatically highlight the topmost result
-  }, options);
-
-  callbacks = $.extend({}, defaultCallbacks, callbacks);
-
-  callbacks.insertSuggestionList($results, $input);
-
-  inputEvents.focus = function() {
-    // If the blur timer is active, a redraw is redundant.
-    preventBlurTimer || redraw(true);
-  };
-
-  inputEvents.blur = function() {
-    // If the blur prevention timer is active, refocus the input, since the
-    // blur event can not be cancelled.
-    if (preventBlurTimer) {
-      $input.focus();
-    }
-    else {
-      // The input has already lost focus, so redraw the suggestion list.
-      redraw();
-    }
-  };
-
-  inputEvents.keydown = function(event) {
-    var index = getHighlightedIndex();
-    // If an arrow key is pressed and a result is highlighted
-    if ($.inArray(event.keyCode, [38, 40]) >= 0 && $results.children().length > 0) {
-      var newIndex,
-        size = $('.result', $results).length;
-      switch (event.keyCode) {
-      case 38: // Up arrow
-        newIndex = Math.max(0, index - 1);
-        break;
-      case 40: // Down arrow
-        newIndex = Math.min(size - 1, index + 1);
-        break;
-      }
-      disableMouseHighlight = true;
-      setHighlighted(newIndex, 'key', true);
-      return false;
-    }
-    // A select key has been pressed
-    else if ($.inArray(event.keyCode, options.selectKeys) >= 0 &&
-             !event.shiftKey && !event.ctrlKey && !event.altKey &&
-             !event.metaKey) {
-      select();
-      return event.keyCode == 9; // Never cancel tab
-    }
-  };
-
-  inputEvents.keyup = inputEvents.click = reprocess;
-
-  $results.delegate('.result', {
-    // When the user hovers a result with the mouse, highlight it.
-    mouseenter: function() {
-      if (disableMouseHighlight) {
-        return;
+  $.fn.betterAutocomplete = function(method) {
+
+    /*
+     * Each method expects the "this" object to be a valid DOM text input node.
+     * The methods "enable", "disable" and "destroy" expects an instance of a
+     * BetterAutocomplete object as their first argument.
+     */
+    var methods = {
+      init: function(resource, options, callbacks) {
+        var $input = $(this),
+            bac = new BetterAutocomplete($input, resource, options, callbacks);
+        $input.data('better-autocomplete', bac);
+        bac.enable();
+      },
+      enable: function(bac) {
+        bac.enable();
+      },
+      disable: function(bac) {
+        bac.disable();
+      },
+      destroy: function(bac) {
+        bac.destroy();
       }
       }
-      setHighlighted($('.result', $results).index($(this)), 'mouse');
-    },
-    mousemove: function() {
-      // Enable mouseover again.
-      disableMouseHighlight = false;
-    },
-    mousedown: function() {
-      select();
-      return false;
-    }
-  });
-
-  // Prevent blur when clicking on group titles, scrollbars etc.,
-  // This event is triggered after the others because of bubbling.
-  $results.mousedown(function() {
-    // Bug in IE where clicking on scrollbar would trigger a blur event for the
-    // input field, despite using preventDefault() on the mousedown event.
-    // This workaround locks the blur event on the input for a small time.
-    clearTimeout(preventBlurTimer);
-    preventBlurTimer = setTimeout(function() {
-      preventBlurTimer = null;
-    }, 50);
-    return false;
-  });
-
-  // If auto highlight is off, remove highlighting
-  $results.mouseleave(function() {
-    if (!options.autoHighlight) {
-      setHighlighted(-1);
-    }
-  });
-
-  /*
-   * PUBLIC METHODS
-   */
-
-  /**
-   * Enable this instance.
-   */
-  this.enable = function() {
-    // Turn off the browser's autocompletion
-    $input
-      .attr('autocomplete', 'OFF')
-      .attr('aria-autocomplete', 'list');
-    $input.bind(inputEvents);
-  };
-
-  /**
-   * Disable this instance.
-   */
-  this.disable = function() {
-    $input
-      .removeAttr('autocomplete')
-      .removeAttr('aria-autocomplete');
-    $results.hide();
-    $input.unbind(inputEvents);
-  };
-
-  /**
-   * Disable and remove this instance. This instance should not be reused.
-   */
-  this.destroy = function() {
-    $results.remove();
-    $input.unbind(inputEvents);
-    $input.removeData('better-autocomplete');
-  };
-
-  /*
-   * PRIVATE METHODS
-   */
+    }, args = Array.prototype.slice.call(arguments, 1);
+
+    // Method calling logic
+    this.each(function() {
+      switch (method) {
+        case 'init':
+          methods[method].apply(this, args);
+          break;
+        case 'enable':
+        case 'disable':
+        case 'destroy':
+          var bac = $(this).data('better-autocomplete');
+          if (bac instanceof BetterAutocomplete) {
+            methods[method].call(this, bac);
+          }
+          break;
+        default:
+          $.error(['Method', method,
+            'does not exist in jQuery.betterAutocomplete.'].join(' '));
+      }
+    });
 
 
-  /**
-   * Add an array of results to the cache. Internal methods always reads from
-   * the cache, so this method must be invoked even when caching is not used,
-   * e.g. when using local results. This method automatically clears as much of
-   * the cache as required to fit within the cache limit.
-   *
-   * @param {String} query
-   *   The query to set the results to.
-   *
-   * @param {Array[Object]} results
-   *   The array of results for this query.
-   */
-  var cacheResults = function(query, results) {
-    cacheSize += results.length;
-    // Now reduce size until it fits
-    while (cacheSize > options.cacheLimit && cacheOrder.length) {
-      var key = cacheOrder.shift();
-      cacheSize -= cache[key].length;
-      delete cache[key];
-    }
-    cacheOrder.push(query);
-    cache[query] = results;
+    // Maintain chainability
+    return this;
   };
   };
 
 
   /**
   /**
-   * Set highlight to a specific result item
+   * The BetterAutocomplete constructor function. Returns a BetterAutocomplete
+   * instance object.
    *
    *
-   * @param {Number} index
-   *   The result item's index, or negative if highlight should be removed.
+   * @private @constructor
+   * @name BetterAutocomplete
    *
    *
-   * @param {String} [trigger]
-   *   What triggered the highlight: "mouse", "key" or "auto". If index is
-   *   negative trigger may be omitted.
-   *
-   * @param {Boolean} [autoScroll]
-   *   (default=false) If scrolling of the results list should be automated.
+   * @param {Object} $input
+   *   A single input element wrapped in jQuery.
    */
    */
-  var setHighlighted = function(index, trigger, autoScroll) {
-    //console.log('Index: '+index)
-    var prevIndex = getHighlightedIndex(),
-      $resultList = $('.result', $results);
-    //console.log('prevIndex: '+prevIndex)
-    $resultList.removeClass('highlight');
-
-    if (index < 0) {
-      return
-    }
-    $resultList.eq(index).addClass('highlight')
+  var BetterAutocomplete = function($input, resource, options, callbacks) {
+
+    var lastRenderedQuery = '',
+        cache = {}, // Key-valued caching of search results
+        cacheOrder = [], // Array of query strings, in the order they are added
+        cacheSize = 0, // Keep count of the cache's size
+        timer, // Used for options.delay
+        activeRemoteCalls = [], // A flat array of query strings that are pending
+        disableMouseHighlight = false, // Suppress the autotriggered mouseover event
+        inputEvents = {},
+        isLocal = ($.type(resource) != 'string'),
+        $results = $('<ul />').addClass('better-autocomplete'),
+        $ariaLive = null,
+        hiddenResults = true, // $results are hidden
+        preventBlurTimer = null; // IE bug workaround, see below in code.
+
+    options = $.extend({
+      charLimit: isLocal ? 1 : 3,
+      delay: 350, // milliseconds
+      caseSensitive: false,
+      cacheLimit: isLocal ? 0 : 256, // Number of result objects
+      remoteTimeout: 10000, // milliseconds
+      crossOrigin: false,
+      selectKeys: [9, 13], // [tab, enter]
+      autoHighlight: true // Automatically highlight the topmost result
+    }, options);
+
+    callbacks = $.extend({}, defaultCallbacks, callbacks);
+
+    callbacks.insertSuggestionList($results, $input);
+
+    inputEvents.focus = function() {
+      // If the blur timer is active, a redraw is redundant.
+      preventBlurTimer || redraw(true);
+    };
+
+    inputEvents.blur = function() {
+      // If the blur prevention timer is active, refocus the input, since the
+      // blur event can not be cancelled.
+      if (preventBlurTimer) {
+        $input.focus();
+      }
+      else {
+        // The input has already lost focus, so redraw the suggestion list.
+        redraw();
+      }
+    };
+
+    inputEvents.keydown = function(event) {
+      var index = getHighlightedIndex();
+      // If an arrow key is pressed and a result is highlighted
+      if ($.inArray(event.keyCode, [38, 40]) >= 0 && $results.children().length > 0) {
+        var newIndex,
+            size = $('.result', $results).length;
+        switch (event.keyCode) {
+          case 38: // Up arrow
+            newIndex = Math.max(-1, index - 1);
+            break;
+          case 40: // Down arrow
+            newIndex = Math.min(size - 1, index + 1);
+            break;
+        }
+        disableMouseHighlight = true;
+        setHighlighted(newIndex, 'key', true);
+        return false;
+      }
+      // A select key has been pressed
+      else if ($.inArray(event.keyCode, options.selectKeys) >= 0 &&
+          !event.shiftKey && !event.ctrlKey && !event.altKey &&
+          !event.metaKey) {
+        select();
+        return event.keyCode == 9; // Never cancel tab
+      }
+    };
 
 
-    if (prevIndex != index) {
-      var result = getResultByIndex(index);
-      callbacks.highlight(result, $input, trigger);
-    }
+    inputEvents.keyup = inputEvents.click = reprocess;
 
 
-    // Scrolling
-    var up = index == 0 || index < prevIndex,
-      $scrollTo = $resultList.eq(index);
+    $results.delegate('.result', {
+      // When the user hovers a result with the mouse, highlight it.
+      mouseenter: function() {
+        if (disableMouseHighlight) {
+          return;
+        }
+        setHighlighted($('.result', $results).index($(this)), 'mouse');
+      },
+      mousemove: function() {
+        // Enable mouseover again.
+        disableMouseHighlight = false;
+      },
+      mousedown: function() {
+        select();
+        return false;
+      }
+    });
 
 
-    if (!autoScroll) {
-      return;
-    }
-    // Scrolling up, then make sure to show the group title
-    if ($scrollTo.prev().is('.group') && up) {
-      $scrollTo = $scrollTo.prev();
-    }
-    // Is $scrollTo partly above the visible region?
-    if ($scrollTo.position().top < 0) {
-      $results.scrollTop($scrollTo.position().top + $results.scrollTop());
-    }
-    // Or is it partly below the visible region?
-    else if (($scrollTo.position().top + $scrollTo.outerHeight()) >
-              $results.height()) {
-      $results.scrollTop($scrollTo.position().top + $results.scrollTop() +
-          $scrollTo.outerHeight() - $results.height());
-    }
-  };
+    // Prevent blur when clicking on group titles, scrollbars etc.,
+    // This event is triggered after the others because of bubbling.
+    $results.mousedown(function() {
+      // Bug in IE where clicking on scrollbar would trigger a blur event for the
+      // input field, despite using preventDefault() on the mousedown event.
+      // This workaround locks the blur event on the input for a small time.
+      clearTimeout(preventBlurTimer);
+      preventBlurTimer = setTimeout(function() {
+        preventBlurTimer = null;
+      }, 50);
+      return false;
+    });
 
 
-  /**
-   * Retrieve the index of the currently highlighted result item
-   *
-   * @returns {Number}
-   *   The result's index or -1 if no result is highlighted.
-   */
-  var getHighlightedIndex = function() {
-    var res = $('.result.highlight', $results)
-    ind= $('.result', $results).index(res);
-    return ind
-  };
+    // If auto highlight is off, remove highlighting
+    $results.mouseleave(function() {
+      if (!options.autoHighlight) {
+        setHighlighted(-1);
+      }
+    });
 
 
-  /**
-   * Retrieve the result object with the specific position in the results list
-   *
-   * @param {Number} index
-   *   The index of the item in the current result list.
-   *
-   * @returns {Object}
-   *   The result object or null if index out of bounds.
-   */
-  var getResultByIndex = function(index) {
-    var $result = $('.result', $results).eq(index);
-    if (!$result.length) {
-      return; // No selectable element
-    }
-    return $result.data('result');
-  };
+    /*
+     * PUBLIC METHODS
+     */
+
+    /**
+     * Enable this instance.
+     */
+    this.enable = function() {
+      // Turn off the browser's autocompletion
+      $input
+          .attr('autocomplete', 'OFF')
+          .attr('aria-autocomplete', 'list')
+          .parent()
+          .attr('role', 'application')
+          .append($('<span class="better-autocomplete-aria"></span>').attr({
+            'aria-live': 'assertive',
+            'id': $input.attr('id') + '-autocomplete-aria-live'
+          }));
+      $input.bind(inputEvents);
+      $ariaLive = $('#' + $input.attr('id') + '-autocomplete-aria-live');
+    };
+
+    /**
+     * Disable this instance.
+     */
+    this.disable = function() {
+      $input
+          .removeAttr('autocomplete')
+          .removeAttr('aria-autocomplete')
+          .parent()
+          .removeAttr('role');
+      $results.hide();
+      $ariaLive.empty();
+      $input.unbind(inputEvents);
+    };
+
+    /**
+     * Disable and remove this instance. This instance should not be reused.
+     */
+    this.destroy = function() {
+      $results.remove();
+      $ariaLive.remove();
+      $input.unbind(inputEvents);
+      $input.removeData('better-autocomplete');
+    };
+
+    /*
+     * PRIVATE METHODS
+     */
+
+    /**
+     * Add an array of results to the cache. Internal methods always reads from
+     * the cache, so this method must be invoked even when caching is not used,
+     * e.g. when using local results. This method automatically clears as much of
+     * the cache as required to fit within the cache limit.
+     *
+     * @param {String} query
+     *   The query to set the results to.
+     *
+     * @param {Array[Object]} results
+     *   The array of results for this query.
+     */
+    var cacheResults = function(query, results) {
+      cacheSize += results.length;
+      // Now reduce size until it fits
+      while (cacheSize > options.cacheLimit && cacheOrder.length) {
+        var key = cacheOrder.shift();
+        cacheSize -= cache[key].length;
+        delete cache[key];
+      }
+      cacheOrder.push(query);
+      cache[query] = results;
+    };
+
+    /**
+     * Set highlight to a specific result item
+     *
+     * @param {Number} index
+     *   The result item's index, or negative if highlight should be removed.
+     *
+     * @param {String} [trigger]
+     *   What triggered the highlight: "mouse", "key" or "auto". If index is
+     *   negative trigger may be omitted.
+     *
+     * @param {Boolean} [autoScroll]
+     *   (default=false) If scrolling of the results list should be automated.
+     */
+    var setHighlighted = function(index, trigger, autoScroll) {
+      //console.log('Index: '+index)
+      var prevIndex = getHighlightedIndex(),
+          $resultList = $('.result', $results);
+      //console.log('prevIndex: '+prevIndex)
+      $resultList.removeClass('highlight');
+
+      if (index < 0) {
+        return
+      }
+      $resultList.eq(index).addClass('highlight')
 
 
-  /**
-   * Select the current highlighted element, if any.
-   */
-  var select = function() {
-    var highlighted = getHighlightedIndex(),
-      result = getResultByIndex(highlighted);
-    callbacks.select(result, $input);
-    // Redraw again, if the callback changed focus or content
-    reprocess();
-  };
+      if (prevIndex != index) {
+        $ariaLive.html($resultList.eq(index).html());
+        var result = getResultByIndex(index);
+        callbacks.highlight(result, $input, trigger);
+      }
 
 
-  /**
-   * Fetch results asynchronously via AJAX.
-   * Errors are ignored.
-   *
-   * @param {String} query
-   *   The query string.
-   */
-  var fetchResults = function(query) {
-    // Synchronously fetch local data
-    if (isLocal) {
-      cacheResults(query, callbacks.queryLocalResults(query, resource,
-                                                      options.caseSensitive));
-      redraw();
-    }
-    // Asynchronously fetch remote data
-    else {
-      activeRemoteCalls.push(query);
-      var url = callbacks.constructURL(resource, query);
-      callbacks.beginFetching($input);
-      callbacks.fetchRemoteData(url, function(data) {
-        var searchResults = callbacks.processRemoteData(data);
-        if (!$.isArray(searchResults)) {
-          searchResults = [];
-        }
-        cacheResults(query, searchResults);
-        // Remove the query from active remote calls, since it's finished
-        activeRemoteCalls = $.grep(activeRemoteCalls, function(value) {
-          return value != query;
-        });
-        if (!activeRemoteCalls.length) {
-          callbacks.finishFetching($input);
-        }
-        redraw();
-      }, options.remoteTimeout, options.crossOrigin);
-    }
-  };
+      // Scrolling
+      var up = index == 0 || index < prevIndex,
+          $scrollTo = $resultList.eq(index);
 
 
-  /**
-   * Reprocess the contents of the input field, fetch data and redraw if
-   * necessary.
-   *
-   * @param {Object} [event]
-   *   The event that triggered the reprocessing. Not always present.
-   */
-  function reprocess(event) {
-    // If this call was triggered by an arrow key, cancel the reprocessing.
-    if ($.type(event) == 'object' && event.type == 'keyup' &&
-        $.inArray(event.keyCode, [38, 40]) >= 0) {
-      return;
-    }
-    var query = callbacks.canonicalQuery($input.val(), options.caseSensitive);
-    clearTimeout(timer);
-    // Indicate that timer is inactive
-    timer = null;
-    redraw();
-    if (query.length >= options.charLimit && !$.isArray(cache[query]) &&
-        $.inArray(query, activeRemoteCalls) == -1) {
-      // Fetching is required
-      $results.empty();
+      if (!autoScroll) {
+        return;
+      }
+      // Scrolling up, then make sure to show the group title
+      if ($scrollTo.prev().is('.group') && up) {
+        $scrollTo = $scrollTo.prev();
+      }
+      // Is $scrollTo partly above the visible region?
+      if ($scrollTo.position().top < 0) {
+        $results.scrollTop($scrollTo.position().top + $results.scrollTop());
+      }
+      // Or is it partly below the visible region?
+      else if (($scrollTo.position().top + $scrollTo.outerHeight()) >
+          $results.height()) {
+        $results.scrollTop($scrollTo.position().top + $results.scrollTop() +
+        $scrollTo.outerHeight() - $results.height());
+      }
+    };
+
+    /**
+     * Retrieve the index of the currently highlighted result item
+     *
+     * @returns {Number}
+     *   The result's index or -1 if no result is highlighted.
+     */
+    var getHighlightedIndex = function() {
+      var res = $('.result.highlight', $results)
+      ind= $('.result', $results).index(res);
+      return ind
+    };
+
+    /**
+     * Retrieve the result object with the specific position in the results list
+     *
+     * @param {Number} index
+     *   The index of the item in the current result list.
+     *
+     * @returns {Object}
+     *   The result object or null if index out of bounds.
+     */
+    var getResultByIndex = function(index) {
+      var $result = $('.result', $results).eq(index);
+      if (!$result.length) {
+        return; // No selectable element
+      }
+      return $result.data('result');
+    };
+
+    /**
+     * Select the current highlighted element, if any.
+     */
+    var select = function() {
+      var highlighted = getHighlightedIndex();
+      if(highlighted >= 0){
+        var result = getResultByIndex(highlighted);
+        callbacks.select(result, $input);
+        // Redraw again, if the callback changed focus or content
+        reprocess();
+      }
+    };
+
+    /**
+     * Fetch results asynchronously via AJAX.
+     * Errors are ignored.
+     *
+     * @param {String} query
+     *   The query string.
+     */
+    var fetchResults = function(query) {
+      // Synchronously fetch local data
       if (isLocal) {
       if (isLocal) {
-        fetchResults(query);
+        cacheResults(query, callbacks.queryLocalResults(query, resource,
+            options.caseSensitive));
+        redraw();
       }
       }
+      // Asynchronously fetch remote data
       else {
       else {
-        timer = setTimeout(function() {
-          fetchResults(query);
-          timer = null;
-        }, options.delay);
+        activeRemoteCalls.push(query);
+        var url = callbacks.constructURL(resource, query);
+        $ariaLive.html('Searching for matches...');
+        callbacks.beginFetching($input);
+        callbacks.fetchRemoteData(url, function(data) {
+          var searchResults = callbacks.processRemoteData(data);
+          if (!$.isArray(searchResults)) {
+            searchResults = [];
+          }
+          cacheResults(query, searchResults);
+          // Remove the query from active remote calls, since it's finished
+          activeRemoteCalls = $.grep(activeRemoteCalls, function(value) {
+            return value != query;
+          });
+          if (!activeRemoteCalls.length) {
+            callbacks.finishFetching($input);
+          }
+          redraw();
+        }, options.remoteTimeout, options.crossOrigin);
       }
       }
-    }
-  };
-
-  /**
-   * Redraws the autocomplete list based on current query and focus.
-   *
-   * @param {Boolean} [focus]
-   *   (default=false) Force to treat the input element like it's focused.
-   */
-  var redraw = function(focus) {
-    var query = callbacks.canonicalQuery($input.val(), options.caseSensitive);
-
-    // The query does not exist in db
-    if (!$.isArray(cache[query])) {
-      lastRenderedQuery = null;
-      $results.empty();
-    }
-    // The query exists and is not already rendered
-    else if (lastRenderedQuery !== query) {
-      lastRenderedQuery = query;
-      renderResults(cache[query]);
-      if (options.autoHighlight && $('.result', $results).length > 0) {
-        setHighlighted(0, 'auto');
+    };
+
+    /**
+     * Reprocess the contents of the input field, fetch data and redraw if
+     * necessary.
+     *
+     * @param {Object} [event]
+     *   The event that triggered the reprocessing. Not always present.
+     */
+    function reprocess(event) {
+      // If this call was triggered by an arrow key, cancel the reprocessing.
+      if ($.type(event) == 'object' && event.type == 'keyup' &&
+          $.inArray(event.keyCode, [38, 40]) >= 0) {
+        return;
       }
       }
-    }
-    // Finally show/hide based on focus and emptiness
-    if (($input.is(':focus') || focus) && !$results.is(':empty')) {
-      $results.filter(':hidden').show() // Show if hidden
-        .scrollTop($results.data('scroll-top')); // Reset the lost scrolling
-      if (hiddenResults) {
-        hiddenResults = false;
-        callbacks.afterShow($results);
+      var query = callbacks.canonicalQuery($input.val(), options.caseSensitive);
+      clearTimeout(timer);
+      // Indicate that timer is inactive
+      timer = null;
+      redraw();
+      if (query.length >= options.charLimit && !$.isArray(cache[query]) &&
+          $.inArray(query, activeRemoteCalls) == -1) {
+        // Fetching is required
+        $results.empty();
+        if (isLocal) {
+          fetchResults(query);
+        }
+        else {
+          timer = setTimeout(function() {
+            fetchResults(query);
+            timer = null;
+          }, options.delay);
+        }
       }
       }
-    }
-    else if ($results.is(':visible')) {
-      // Store the scrolling position for later
-      $results.data('scroll-top', $results.scrollTop())
-        .hide(); // Hiding it resets it's scrollTop
-      if (!hiddenResults) {
-        hiddenResults = true;
-        callbacks.afterHide($results);
+    };
+
+    /**
+     * Redraws the autocomplete list based on current query and focus.
+     *
+     * @param {Boolean} [focus]
+     *   (default=false) Force to treat the input element like it's focused.
+     */
+    var redraw = function(focus) {
+      var query = callbacks.canonicalQuery($input.val(), options.caseSensitive);
+
+      // The query does not exist in db
+      if (!$.isArray(cache[query])) {
+        lastRenderedQuery = null;
+        $results.empty();
       }
       }
-    }
-  };
-
-  /**
-   * Regenerate the DOM content within the results list for a given set of
-   * results. Heavy method, use only when necessary.
-   *
-   * @param {Array[Object]} results
-   *   An array of result objects to render.
-   */
-  var renderResults = function(results) {
-    $results.empty();
-    var groups = {}; // Key is the group name, value is the heading element.
-
-    $.each(results, function(index, result) {
-      if ($.type(result) != 'object') {
-        return; // Continue
+      // The query exists and is not already rendered
+      else if (lastRenderedQuery !== query) {
+        lastRenderedQuery = query;
+        renderResults(cache[query]);
       }
       }
-
-      var output = callbacks.themeResult(result);
-      if ($.type(output) != 'string') {
-        return; // Continue
+      // Finally show/hide based on focus and emptiness
+      if (($input.is(':focus') || focus) && !$results.is(':empty')) {
+        $results.filter(':hidden').show() // Show if hidden
+            .scrollTop($results.data('scroll-top')); // Reset the lost scrolling
+        if (hiddenResults) {
+          hiddenResults = false;
+          $ariaLive.html('Autocomplete popup');
+          if (options.autoHighlight && $('.result', $results).length > 0) {
+            setHighlighted(0, 'auto');
+          }
+          callbacks.afterShow($results);
+        }
       }
       }
-
-      // Add the group if it doesn't exist
-      var group = callbacks.getGroup(result);
-      if ($.type(group) == 'string' && !groups[group]) {
-        var $groupHeading = $('<li />').addClass('group')
-          .append($('<h3 />').html(group))
-          .appendTo($results);
-        groups[group] = $groupHeading;
+      else if ($results.is(':visible')) {
+        // Store the scrolling position for later
+        $results.data('scroll-top', $results.scrollTop())
+            .hide(); // Hiding it resets it's scrollTop
+        if (!hiddenResults) {
+          hiddenResults = true;
+          $ariaLive.empty();
+          callbacks.afterHide($results);
+        }
       }
       }
+    };
+
+    /**
+     * Regenerate the DOM content within the results list for a given set of
+     * results. Heavy method, use only when necessary.
+     *
+     * @param {Array[Object]} results
+     *   An array of result objects to render.
+     */
+    var renderResults = function(results) {
+      $results.empty();
+      var groups = {}; // Key is the group name, value is the heading element.
 
 
-      var $result = $('<li />').addClass('result')
-        .append(output)
-        .data('result', result) // Store the result object on this DOM element
-        .addClass(result.addClass);
+      $.each(results, function(index, result) {
+        if ($.type(result) != 'object') {
+          return; // Continue
+        }
 
 
-      // First groupless item
-      if ($.type(group) != 'string' &&
-          !$results.children().first().is('.result')) {
-        $results.prepend($result);
-        return; // Continue
-      }
-      var $traverseFrom = ($.type(group) == 'string') ?
-                          groups[group] : $results.children().first();
-      var $target = $traverseFrom.nextUntil('.group').last();
-      $result.insertAfter($target.length ? $target : $traverseFrom);
-    });
-  };
-};
+        var output = callbacks.themeResult(result);
+        if ($.type(output) != 'string') {
+          return; // Continue
+        }
 
 
-/*
- * CALLBACK METHODS
- */
+        // Add the group if it doesn't exist
+        var group = callbacks.getGroup(result);
+        if ($.type(group) == 'string' && !groups[group]) {
+          var $groupHeading = $('<li />').addClass('group')
+              .append($('<h3 />').html(group))
+              .appendTo($results);
+          groups[group] = $groupHeading;
+        }
 
 
-/**
- * These callbacks are supposed to be overridden by you when you need
- * customization of the default behavior. When you are overriding a callback
- * function, it is a good idea to copy the source code from the default
- * callback function, as a skeleton.
- *
- * @name callbacks
- * @namespace
- */
-var defaultCallbacks = {
-  /**
-   * @lends callbacks.prototype
-   */
+        var $result = $('<li />').addClass('result')
+            .append(output)
+            .data('result', result) // Store the result object on this DOM element
+            .addClass(result.addClass);
 
 
-  /**
-   * Gets fired when the user selects a result by clicking or using the
-   * keyboard to select an element.
-   *
-   * <br /><br /><em>Default behavior: Inserts the result's title into the
-   * input field.</em>
-   *
-   * @param {Object} result
-   *   The result object that was selected.
-   *
-   * @param {Object} $input
-   *   The input DOM element, wrapped in jQuery.
-   */
-  select: function(result, $input) {
-    $input.val(result.title);
-  },
+        // First groupless item
+        if ($.type(group) != 'string' &&
+            !$results.children().first().is('.result')) {
+          $results.prepend($result);
+          return; // Continue
+        }
+        var $traverseFrom = ($.type(group) == 'string') ?
+            groups[group] : $results.children().first();
+        var $target = $traverseFrom.nextUntil('.group').last();
+        $result.insertAfter($target.length ? $target : $traverseFrom);
+      });
+    };
+  };
 
 
-  /**
-   * Gets fired when the a result is highlighted. This may happen either
-   * automatically or by user action.
-   *
-   * <br /><br /><em>Default behavior: Does nothing.</em>
-   *
-   * @param {Object} result
-   *   The result object that was selected.
-   *
-   * @param {Object} $input
-   *   The input DOM element, wrapped in jQuery.
-   *
-   * @param {String} trigger
-   *   The event which triggered the highlighting. Must be one of the
-   *   following:
-   *   <ul><li>
-   *     "mouse": A mouseover event triggered the highlighting.
-   *   </li><li>
-   *     "key": The user pressed an arrow key to navigate amongst the results.
-   *   </li><li>
-   *     "auto": If options.autoHighlight is set, an automatic highlight of the
-   *     first result will occur each time a new result set is rendered.
-   *   </li></ul>
+  /*
+   * CALLBACK METHODS
    */
    */
-  highlight: function(result, $input, trigger) {
-    // Does nothing
-  },
 
 
   /**
   /**
-   * Given a result object, theme it to HTML.
-   *
-   * <br /><br /><em>Default behavior: Wraps result.title in an h4 tag, and
-   * result.description in a p tag. Note that no sanitization of malicious
-   * scripts is done here. Whatever is within the title/description is just
-   * printed out. May contain HTML.</em>
-   *
-   * @param {Object} result
-   *   The result object that should be rendered.
+   * These callbacks are supposed to be overridden by you when you need
+   * customization of the default behavior. When you are overriding a callback
+   * function, it is a good idea to copy the source code from the default
+   * callback function, as a skeleton.
    *
    *
-   * @returns {String}
-   *   HTML output, will be wrapped in a list element.
+   * @name callbacks
+   * @namespace
    */
    */
-  themeResult: function(result) {
-    var output = [];
-    if ($.type(result.title) == 'string') {
-      output.push('<h4>', result.title, '</h4>');
-    }
-    if ($.type(result.description) == 'string') {
-      output.push('<p>', result.description, '</p>');
-    }
-    return output.join('');
-  },
+  var defaultCallbacks = {
+    /**
+     * @lends callbacks.prototype
+     */
+
+    /**
+     * Gets fired when the user selects a result by clicking or using the
+     * keyboard to select an element.
+     *
+     * <br /><br /><em>Default behavior: Inserts the result's title into the
+     * input field.</em>
+     *
+     * @param {Object} result
+     *   The result object that was selected.
+     *
+     * @param {Object} $input
+     *   The input DOM element, wrapped in jQuery.
+     */
+    select: function(result, $input) {
+      $input.val(result.title);
+    },
 
 
-  /**
-   * Retrieve local results from the local resource by providing a query
-   * string.
-   *
-   * <br /><br /><em>Default behavior: Automatically handles arrays, if the
-   * data inside each element is either a plain string or a result object.
-   * If it is a result object, it will match the query string against the
-   * title and description property. Search is not case sensitive.</em>
-   *
-   * @param {String} query
-   *   The query string, unescaped. May contain any UTF-8 character.
-   *   If case insensitive, it already is lowercased.
-   *
-   * @param {Object} resource
-   *   The resource provided in the {@link jQuery.betterAutocomplete} init
-   *   constructor.
-   *
-   * @param {Boolean} caseSensitive
-   *   From options.caseSensitive, the searching should be case sensitive.
-   *
-   * @returns {Array[Object]}
-   *   A flat array containing pure result objects. May be an empty array.
-   */
-  queryLocalResults: function(query, resource, caseSensitive) {
-    if (!$.isArray(resource)) {
-      // Per default Better Autocomplete only handles arrays
-      return [];
-    }
-    var results = [];
-    $.each(resource, function(i, value) {
-      switch ($.type(value)) {
-      case 'string': // Flat array of strings
-        if ((caseSensitive ? value : value.toLowerCase())
-            .indexOf(query) >= 0) {
-          // Match found
-          results.push({ title: value });
-        }
-        break;
-      case 'object': // Array of result objects
-        if ($.type(value.title) == 'string' &&
-            (caseSensitive ? value.title : value.title.toLowerCase())
-            .indexOf(query) >= 0) {
-          // Match found in title field
-          results.push(value);
-        }
-        else if ($.type(value.description) == 'string' &&
-                 (caseSensitive ? value.description :
-                 value.description.toLowerCase()).indexOf(query) >= 0) {
-          // Match found in description field
-          results.push(value);
-        }
-        break;
+    /**
+     * Gets fired when the a result is highlighted. This may happen either
+     * automatically or by user action.
+     *
+     * <br /><br /><em>Default behavior: Does nothing.</em>
+     *
+     * @param {Object} result
+     *   The result object that was selected.
+     *
+     * @param {Object} $input
+     *   The input DOM element, wrapped in jQuery.
+     *
+     * @param {String} trigger
+     *   The event which triggered the highlighting. Must be one of the
+     *   following:
+     *   <ul><li>
+     *     "mouse": A mouseover event triggered the highlighting.
+     *   </li><li>
+     *     "key": The user pressed an arrow key to navigate amongst the results.
+     *   </li><li>
+     *     "auto": If options.autoHighlight is set, an automatic highlight of the
+     *     first result will occur each time a new result set is rendered.
+     *   </li></ul>
+     */
+    highlight: function(result, $input, trigger) {
+      // Does nothing
+    },
+
+    /**
+     * Given a result object, theme it to HTML.
+     *
+     * <br /><br /><em>Default behavior: Wraps result.title in an h4 tag, and
+     * result.description in a p tag. Note that no sanitization of malicious
+     * scripts is done here. Whatever is within the title/description is just
+     * printed out. May contain HTML.</em>
+     *
+     * @param {Object} result
+     *   The result object that should be rendered.
+     *
+     * @returns {String}
+     *   HTML output, will be wrapped in a list element.
+     */
+    themeResult: function(result) {
+      var output = [];
+      if ($.type(result.title) == 'string') {
+        output.push('<h4>', result.title, '</h4>');
       }
       }
-    });
-    return results;
-  },
+      if ($.type(result.description) == 'string') {
+        output.push('<p>', result.description, '</p>');
+      }
+      return output.join('');
+    },
 
 
-  /**
-   * Fetch remote result data and return it using completeCallback when
-   * fetching is finished. Must be asynchronous in order to not freeze the
-   * Better Autocomplete instance.
-   *
-   * <br /><br /><em>Default behavior: Fetches JSON data from the url, using
-   * the jQuery.ajax() method. Errors are ignored.</em>
-   *
-   * @param {String} url
-   *   The URL to fetch data from.
-   *
-   * @param {Function} completeCallback
-   *   This function must be called, even if an error occurs. It takes zero
-   *   or one parameter: the data that was fetched.
-   *
-   * @param {Number} timeout
-   *   The preferred timeout for the request. This callback should respect
-   *   the timeout.
-   *
-   * @param {Boolean} crossOrigin
-   *   True if a cross origin request should be performed.
-   */
-  fetchRemoteData: function(url, completeCallback, timeout, crossOrigin) {
-    $.ajax({
-      url: url,
-      dataType: crossOrigin && !$.support.cors ? 'jsonp' : 'json',
-      timeout: timeout,
-      success: function(data, textStatus) {
-        completeCallback(data);
-      },
-      error: function(jqXHR, textStatus, errorThrown) {
-        completeCallback();
+    /**
+     * Retrieve local results from the local resource by providing a query
+     * string.
+     *
+     * <br /><br /><em>Default behavior: Automatically handles arrays, if the
+     * data inside each element is either a plain string or a result object.
+     * If it is a result object, it will match the query string against the
+     * title and description property. Search is not case sensitive.</em>
+     *
+     * @param {String} query
+     *   The query string, unescaped. May contain any UTF-8 character.
+     *   If case insensitive, it already is lowercased.
+     *
+     * @param {Object} resource
+     *   The resource provided in the {@link jQuery.betterAutocomplete} init
+     *   constructor.
+     *
+     * @param {Boolean} caseSensitive
+     *   From options.caseSensitive, the searching should be case sensitive.
+     *
+     * @returns {Array[Object]}
+     *   A flat array containing pure result objects. May be an empty array.
+     */
+    queryLocalResults: function(query, resource, caseSensitive) {
+      if (!$.isArray(resource)) {
+        // Per default Better Autocomplete only handles arrays
+        return [];
       }
       }
-    });
-  },
+      var results = [];
+      $.each(resource, function(i, value) {
+        switch ($.type(value)) {
+          case 'string': // Flat array of strings
+            if ((caseSensitive ? value : value.toLowerCase())
+                    .indexOf(query) >= 0) {
+              // Match found
+              results.push({ title: value });
+            }
+            break;
+          case 'object': // Array of result objects
+            if ($.type(value.title) == 'string' &&
+                (caseSensitive ? value.title : value.title.toLowerCase())
+                    .indexOf(query) >= 0) {
+              // Match found in title field
+              results.push(value);
+            }
+            else if ($.type(value.description) == 'string' &&
+                (caseSensitive ? value.description :
+                    value.description.toLowerCase()).indexOf(query) >= 0) {
+              // Match found in description field
+              results.push(value);
+            }
+            break;
+        }
+      });
+      return results;
+    },
 
 
-  /**
-   * Process remote fetched data by extracting an array of result objects
-   * from it. This callback is useful if the fetched data is not the plain
-   * results array, but a more complicated object which does contain results.
-   *
-   * <br /><br /><em>Default behavior: If the data is defined and is an
-   * array, return it. Otherwise return an empty array.</em>
-   *
-   * @param {mixed} data
-   *   The raw data recieved from the server. Can be undefined.
-   *
-   * @returns {Array[Object]}
-   *   A flat array containing result objects. May be an empty array.
-   */
-  processRemoteData: function(data) {
-    if ($.isArray(data)) {
-      return data;
-    }
-    else {
-      return [];
-    }
-  },
+    /**
+     * Fetch remote result data and return it using completeCallback when
+     * fetching is finished. Must be asynchronous in order to not freeze the
+     * Better Autocomplete instance.
+     *
+     * <br /><br /><em>Default behavior: Fetches JSON data from the url, using
+     * the jQuery.ajax() method. Errors are ignored.</em>
+     *
+     * @param {String} url
+     *   The URL to fetch data from.
+     *
+     * @param {Function} completeCallback
+     *   This function must be called, even if an error occurs. It takes zero
+     *   or one parameter: the data that was fetched.
+     *
+     * @param {Number} timeout
+     *   The preferred timeout for the request. This callback should respect
+     *   the timeout.
+     *
+     * @param {Boolean} crossOrigin
+     *   True if a cross origin request should be performed.
+     */
+    fetchRemoteData: function(url, completeCallback, timeout, crossOrigin) {
+      $.ajax({
+        url: url,
+        dataType: crossOrigin && !$.support.cors ? 'jsonp' : 'json',
+        timeout: timeout,
+        success: function(data, textStatus) {
+          completeCallback(data);
+        },
+        error: function(jqXHR, textStatus, errorThrown) {
+          completeCallback();
+        }
+      });
+    },
 
 
-  /**
-   * From a given result object, return it's group name (if any). Used for
-   * grouping results together.
-   *
-   * <br /><br /><em>Default behavior: If the result has a "group" property
-   * defined, return it.</em>
-   *
-   * @param {Object} result
-   *   The result object.
-   *
-   * @returns {String}
-   *   The group name, may contain HTML. If no group, don't return anything.
-   */
-  getGroup: function(result) {
-    if ($.type(result.group) == 'string') {
-      return result.group;
-    }
-  },
+    /**
+     * Process remote fetched data by extracting an array of result objects
+     * from it. This callback is useful if the fetched data is not the plain
+     * results array, but a more complicated object which does contain results.
+     *
+     * <br /><br /><em>Default behavior: If the data is defined and is an
+     * array, return it. Otherwise return an empty array.</em>
+     *
+     * @param {mixed} data
+     *   The raw data recieved from the server. Can be undefined.
+     *
+     * @returns {Array[Object]}
+     *   A flat array containing result objects. May be an empty array.
+     */
+    processRemoteData: function(data) {
+      if ($.isArray(data)) {
+        return data;
+      }
+      else {
+        return [];
+      }
+    },
 
 
-  /**
-   * Called when remote fetching begins.
-   *
-   * <br /><br /><em>Default behavior: Adds the CSS class "fetching" to the
-   * input field, for styling purposes.</em>
-   *
-   * @param {Object} $input
-   *   The input DOM element, wrapped in jQuery.
-   */
-  beginFetching: function($input) {
-    $input.addClass('fetching');
-  },
+    /**
+     * From a given result object, return it's group name (if any). Used for
+     * grouping results together.
+     *
+     * <br /><br /><em>Default behavior: If the result has a "group" property
+     * defined, return it.</em>
+     *
+     * @param {Object} result
+     *   The result object.
+     *
+     * @returns {String}
+     *   The group name, may contain HTML. If no group, don't return anything.
+     */
+    getGroup: function(result) {
+      if ($.type(result.group) == 'string') {
+        return result.group;
+      }
+    },
 
 
-  /**
-   * Called when fetching is finished. All active requests must finish before
-   * this function is called.
-   *
-   * <br /><br /><em>Default behavior: Removes the "fetching" class.</em>
-   *
-   * @param {Object} $input
-   *   The input DOM element, wrapped in jQuery.
-   */
-  finishFetching: function($input) {
-    $input.removeClass('fetching');
-  },
+    /**
+     * Called when remote fetching begins.
+     *
+     * <br /><br /><em>Default behavior: Adds the CSS class "fetching" to the
+     * input field, for styling purposes.</em>
+     *
+     * @param {Object} $input
+     *   The input DOM element, wrapped in jQuery.
+     */
+    beginFetching: function($input) {
+      $input.addClass('fetching');
+    },
 
 
-  /**
-   * Executed after the suggestion list has been shown.
-   *
-   * @param {Object} $results
-   *   The suggestion list UL element, wrapped in jQuery.
-   *
-   * <br /><br /><em>Default behavior: Does nothing.</em>
-   */
-  afterShow: function($results) {},
+    /**
+     * Called when fetching is finished. All active requests must finish before
+     * this function is called.
+     *
+     * <br /><br /><em>Default behavior: Removes the "fetching" class.</em>
+     *
+     * @param {Object} $input
+     *   The input DOM element, wrapped in jQuery.
+     */
+    finishFetching: function($input) {
+      $input.removeClass('fetching');
+    },
 
 
-  /**
-   * Executed after the suggestion list has been hidden.
-   *
-   * @param {Object} $results
-   *   The suggestion list UL element, wrapped in jQuery.
-   *
-   * <br /><br /><em>Default behavior: Does nothing.</em>
-   */
-  afterHide: function($results) {},
+    /**
+     * Executed after the suggestion list has been shown.
+     *
+     * @param {Object} $results
+     *   The suggestion list UL element, wrapped in jQuery.
+     *
+     * <br /><br /><em>Default behavior: Does nothing.</em>
+     */
+    afterShow: function($results) {},
+
+    /**
+     * Executed after the suggestion list has been hidden.
+     *
+     * @param {Object} $results
+     *   The suggestion list UL element, wrapped in jQuery.
+     *
+     * <br /><br /><em>Default behavior: Does nothing.</em>
+     */
+    afterHide: function($results) {},
+
+    /**
+     * Construct the remote fetching URL.
+     *
+     * <br /><br /><em>Default behavior: Adds "?q=<query>" or "&q=<query>" to the
+     * path. The query string is URL encoded.</em>
+     *
+     * @param {String} path
+     *   The path given in the {@link jQuery.betterAutocomplete} constructor.
+     *
+     * @param {String} query
+     *   The raw query string. Remember to URL encode this to prevent illegal
+     *   character errors.
+     *
+     * @returns {String}
+     *   The URL, ready for fetching.
+     */
+    constructURL: function(path, query) {
+      return path + (path.indexOf('?') > -1 ? '&' : '?') + 'q=' + encodeURIComponent(query);
+    },
 
 
-  /**
-   * Construct the remote fetching URL.
-   *
-   * <br /><br /><em>Default behavior: Adds "?q=<query>" or "&q=<query>" to the
-   * path. The query string is URL encoded.</em>
-   *
-   * @param {String} path
-   *   The path given in the {@link jQuery.betterAutocomplete} constructor.
-   *
-   * @param {String} query
-   *   The raw query string. Remember to URL encode this to prevent illegal
-   *   character errors.
-   *
-   * @returns {String}
-   *   The URL, ready for fetching.
-   */
-  constructURL: function(path, query) {
-    return path + (path.indexOf('?') > -1 ? '&' : '?') + 'q=' + encodeURIComponent(query);
-  },
+    /**
+     * To ease up on server load, treat similar strings the same.
+     *
+     * <br /><br /><em>Default behavior: Trims the query from leading and
+     * trailing whitespace.</em>
+     *
+     * @param {String} rawQuery
+     *   The user's raw input.
+     *
+     * @param {Boolean} caseSensitive
+     *   Case sensitive. Will convert to lowercase if false.
+     *
+     * @returns {String}
+     *   The canonical query associated with this string.
+     */
+    canonicalQuery: function(rawQuery, caseSensitive) {
+      var query = $.trim(rawQuery);
+      if (!caseSensitive) {
+        query = query.toLowerCase();
+      }
+      return query;
+    },
 
 
-  /**
-   * To ease up on server load, treat similar strings the same.
-   *
-   * <br /><br /><em>Default behavior: Trims the query from leading and
-   * trailing whitespace.</em>
-   *
-   * @param {String} rawQuery
-   *   The user's raw input.
-   *
-   * @param {Boolean} caseSensitive
-   *   Case sensitive. Will convert to lowercase if false.
-   *
-   * @returns {String}
-   *   The canonical query associated with this string.
-   */
-  canonicalQuery: function(rawQuery, caseSensitive) {
-    var query = $.trim(rawQuery);
-    if (!caseSensitive) {
-      query = query.toLowerCase();
+    /**
+     * Insert the results list into the DOM and position it properly.
+     *
+     * <br /><br /><em>Default behavior: Inserts suggestion list directly
+     * after the input element and sets an absolute position using
+     * jQuery.position() for determining left/top values. Also adds a nice
+     * looking box-shadow to the list.</em>
+     *
+     * @param {Object} $results
+     *   The UL list element to insert, wrapped in jQuery.
+     *
+     * @param {Object} $input
+     *   The text input element, wrapped in jQuery.
+     */
+    insertSuggestionList: function($results, $input) {
+      $results.width($input.outerWidth() - 2) // Subtract border width.
+          .css({
+            position: 'absolute',
+            left: $input.position().left,
+            top: $input.position().top + $input.outerHeight()
+          })
+          .hide()
+          .insertAfter($input);
     }
     }
-    return query;
-  },
+  };
 
 
-  /**
-   * Insert the results list into the DOM and position it properly.
-   *
-   * <br /><br /><em>Default behavior: Inserts suggestion list directly
-   * after the input element and sets an absolute position using
-   * jQuery.position() for determining left/top values. Also adds a nice
-   * looking box-shadow to the list.</em>
-   *
-   * @param {Object} $results
-   *   The UL list element to insert, wrapped in jQuery.
+  /*
+   * jQuery focus selector, required by Better Autocomplete.
    *
    *
-   * @param {Object} $input
-   *   The text input element, wrapped in jQuery.
+   * @see http://stackoverflow.com/questions/967096/using-jquery-to-test-if-an-input-has-focus/2684561#2684561
    */
    */
-  insertSuggestionList: function($results, $input) {
-    $results.width($input.outerWidth() - 2) // Subtract border width.
-      .css({
-        position: 'absolute',
-        zIndex: 10,
-        maxHeight: '330px',
-        // Visually indicate that results are in the topmost layer
-        boxShadow: '0 0 15px rgba(0, 0, 0, 0.5)'
-      })
-      .hide()
-      .insertAfter($input);
+  var filters = $.expr[':'];
+  if (!filters.focus) {
+    filters.focus = function(elem) {
+      return elem === document.activeElement && (elem.type || elem.href);
+    };
   }
   }
-};
-
-/*
- * jQuery focus selector, required by Better Autocomplete.
- *
- * @see http://stackoverflow.com/questions/967096/using-jquery-to-test-if-an-input-has-focus/2684561#2684561
- */
-var filters = $.expr[':'];
-if (!filters.focus) {
-  filters.focus = function(elem) {
-    return elem === document.activeElement && (elem.type || elem.href);
-  };
-}
 
 
 })(jQuery);
 })(jQuery);

+ 7 - 1
sites/all/modules/contrib/admin/linkit/css/linkit.css

@@ -172,6 +172,8 @@
 .better-autocomplete {
 .better-autocomplete {
   background-color: transparent;
   background-color: transparent;
   border-bottom: none;
   border-bottom: none;
+  box-shadow: none;
+  z-index: inherit;
 }
 }
 .better-autocomplete > .result.status-ok {
 .better-autocomplete > .result.status-ok {
   background: #397928;
   background: #397928;
@@ -219,4 +221,8 @@
   border-bottom-left-radius: 3px;
   border-bottom-left-radius: 3px;
   border-bottom-right-radius: 3px;
   border-bottom-right-radius: 3px;
   border-bottom: 1px solid #BFBFBF;
   border-bottom: 1px solid #BFBFBF;
-}
+}
+
+.ui-dialog.ui-front.linkit-wrapper {
+  z-index: 2000;
+}

+ 1 - 1
sites/all/modules/contrib/admin/linkit/editors/ckeditor/linkitDialog.js

@@ -17,7 +17,7 @@ Drupal.linkit.registerDialogHelper('ckeditor', {
    */
    */
   afterInit : function () {
   afterInit : function () {
      var editor = Drupal.settings.linkit.currentInstance.editor;
      var editor = Drupal.settings.linkit.currentInstance.editor;
-     var element = CKEDITOR.plugins.link.getSelectedLink( editor );
+     var element = CKEDITOR.plugins.link.getSelectedLink(editor);
 
 
     // If we have selected a link element, lets populate the fields in the
     // If we have selected a link element, lets populate the fields in the
     // modal with the values from that link element.
     // modal with the values from that link element.

+ 26 - 10
sites/all/modules/contrib/admin/linkit/editors/ckeditor/plugin.js

@@ -41,8 +41,20 @@
 
 
           // Set the editor object.
           // Set the editor object.
           Drupal.settings.linkit.currentInstance.editor = editor;
           Drupal.settings.linkit.currentInstance.editor = editor;
-          // Set profile.
-          Drupal.settings.linkit.currentInstance.profile = Drupal.settings.linkit.fields[editor.name].profile;
+          // Find the current input format of the field we're looking at.
+          var format = '';
+          if (Drupal.wysiwyg) { // If using the WYSIWYG module with CKEditor as the editor
+            // Note that WYSIWYG prepends "profile" to the profile name, so we use .substring() to remove the "format".
+            format = Drupal.wysiwyg.instances[editor.name].format.substring(6);
+          }
+          else if (Drupal.settings.ckeditor) { // If using the CKEditor module
+            format = Drupal.settings.ckeditor.elements[editor.name];
+          } else {
+            alert(Drupal.t('Could not find the Linkit profile.'));
+            return;
+          }
+          // Set profile based on the current text format of this field.
+          Drupal.settings.linkit.currentInstance.profile = Drupal.settings.linkit.formats[format].profile;
           // Set the name of the source field.
           // Set the name of the source field.
           Drupal.settings.linkit.currentInstance.source = editor.name;
           Drupal.settings.linkit.currentInstance.source = editor.name;
           // Set the source type.
           // Set the source type.
@@ -108,18 +120,22 @@
 
 
       // Add a shortcut. Only CKeditor version 4 has this function.
       // Add a shortcut. Only CKeditor version 4 has this function.
       if (version >= 4) {
       if (version >= 4) {
-        editor.setKeystroke( CKEDITOR.CTRL + 76 /*L*/, 'linkit' );
+        editor.setKeystroke(CKEDITOR.CTRL + 76 /*L*/, 'linkit');
       }
       }
 
 
       // Add event listener.
       // Add event listener.
-      editor.on( 'doubleclick', function( evt ) {
+      editor.on('doubleclick', function(evt) {
+        if (evt.data.dialog !== 'link') {
+          return;
+        }
+
         // Delete the default link dialog.
         // Delete the default link dialog.
         delete evt.data.dialog;
         delete evt.data.dialog;
 
 
-        var element = CKEDITOR.plugins.link.getSelectedLink( editor ) || evt.data.element;
-        if ( !element.isReadOnly() ) {
-          if ( element.is( 'a' ) ) {
-            editor.getSelection().selectElement( element );
+        var element = CKEDITOR.plugins.link.getSelectedLink(editor) || evt.data.element;
+        if (!element.isReadOnly()) {
+          if (element.is( 'a' )) {
+            editor.getSelection().selectElement(element);
             if (version >= 4) {
             if (version >= 4) {
               editor.commands.linkit.exec();
               editor.commands.linkit.exec();
             }
             }
@@ -131,7 +147,7 @@
       });
       });
 
 
       // Register an extra fucntion, this will be used in the modal.
       // Register an extra fucntion, this will be used in the modal.
-      editor._.linkitFnNum = CKEDITOR.tools.addFunction( insertLink, editor );
+      editor._.linkitFnNum = CKEDITOR.tools.addFunction(insertLink, editor);
     }
     }
   });
   });
 
 
@@ -150,7 +166,7 @@
       var range = selection.getRanges(1)[0];
       var range = selection.getRanges(1)[0];
       if (range.collapsed) {
       if (range.collapsed) {
         var content = (Drupal.settings.linkit.currentInstance.linkContent) ? Drupal.settings.linkit.currentInstance.linkContent : data.path;
         var content = (Drupal.settings.linkit.currentInstance.linkContent) ? Drupal.settings.linkit.currentInstance.linkContent : data.path;
-        var text = new CKEDITOR.dom.text(content , editor.document );
+        var text = new CKEDITOR.dom.text(content , editor.document);
         range.insertNode(text);
         range.insertNode(text);
         range.selectNodeContents(text);
         range.selectNodeContents(text);
       }
       }

+ 12 - 2
sites/all/modules/contrib/admin/linkit/editors/tinymce/editor_plugin.js

@@ -17,8 +17,18 @@
 
 
         // Set the editor object.
         // Set the editor object.
         Drupal.settings.linkit.currentInstance.editor = editor;
         Drupal.settings.linkit.currentInstance.editor = editor;
-        // Set profile.
-        Drupal.settings.linkit.currentInstance.profile = Drupal.settings.linkit.fields[editor.id].profile;
+
+        // Find the current input format of the field we're looking at. Note that we get it in the form
+        // "format<formatname" instead of just "<formatname>" so we use .substring() to remove the "format".
+        if (Drupal.wysiwyg && Drupal.wysiwyg.instances[editor.id].format) {
+          var format = Drupal.wysiwyg.instances[editor.id].format.substring(6);
+        } else {
+          alert(Drupal.t('Could not find the Linkit profile.'));
+          return;
+        }
+
+        // Set profile based on the current text format of this field.
+        Drupal.settings.linkit.currentInstance.profile = Drupal.settings.linkit.formats[format].profile;
 
 
         // Set the name of the source field..
         // Set the name of the source field..
         Drupal.settings.linkit.currentInstance.source = editor.id;
         Drupal.settings.linkit.currentInstance.source = editor.id;

+ 5 - 6
sites/all/modules/contrib/admin/linkit/js/linkit.dashboard.js

@@ -9,13 +9,13 @@ Drupal.behaviors.linkitDashboard = {
   attach: function (context, settings) {
   attach: function (context, settings) {
     // Bind the insert link button.
     // Bind the insert link button.
     $('.linkit-insert', context).once('linkit-insert', function() {
     $('.linkit-insert', context).once('linkit-insert', function() {
-      $('.linkit-insert', context).click(function() {
+      $('.linkit-insert', context).click(function(event) {
+        event.preventDefault();
         // Call the insertLink() function.
         // Call the insertLink() function.
         Drupal.linkit.getDialogHelper(Drupal.settings.linkit.currentInstance.helper).insertLink(Drupal.linkit.getLink());
         Drupal.linkit.getDialogHelper(Drupal.settings.linkit.currentInstance.helper).insertLink(Drupal.linkit.getLink());
 
 
         // Close the dialog.
         // Close the dialog.
         Drupal.linkit.modalClose();
         Drupal.linkit.modalClose();
-        return false;
       });
       });
     });
     });
 
 
@@ -232,12 +232,11 @@ Drupal.behaviors.linkitSearch = {
 
 
           $('.linkit-path-element', context).focus();
           $('.linkit-path-element', context).focus();
         }
         }
-      }
+      };
 
 
       searchElement.betterAutocomplete('init', Drupal.settings.linkit.currentInstance.autocompletePathParsed, Drupal.settings.linkit.currentInstance.autocomplete, callbacks);
       searchElement.betterAutocomplete('init', Drupal.settings.linkit.currentInstance.autocompletePathParsed, Drupal.settings.linkit.currentInstance.autocomplete, callbacks);
     });
     });
   }
   }
 };
 };
-
-
-})(jQuery);
+  
+})(jQuery);

+ 136 - 121
sites/all/modules/contrib/admin/linkit/js/linkit.field.js

@@ -2,134 +2,149 @@
  * @file
  * @file
  * Linkit field ui functions
  * Linkit field ui functions
  */
  */
-
-(function ($) {
-
-Drupal.behaviors.linkit_field = {
-  attach : function(context, settings) {
-    // If there is no fields, just stop here.
-
-    if (settings.linkit == undefined || settings.linkit.fields == null) {
-      return false;
-    }
-
-    $.each(settings.linkit.fields, function(field_name, field) {
-      $('#' + field_name, context).once('linkit_field', function() {
-        $('.linkit-field-' + field_name).click(function() {
-          // Set profile.
-          Drupal.settings.linkit.currentInstance.profile = Drupal.settings.linkit.fields[field_name].profile;
-
-          // Set the name of the source field..
-          Drupal.settings.linkit.currentInstance.source = field_name;
-
-          // Set the source type.
-          Drupal.settings.linkit.currentInstance.helper = 'field';
-
-          // Only care about selection if the element is a textarea.
-          if ($('textarea#' + field_name).length) {
-            var selection =  Drupal.linkit.getDialogHelper('field').getSelection($('#' + field_name).get(0));
-            // Save the selection.
-            Drupal.settings.linkit.currentInstance.selection = selection;
-          }
-
-          // Suppress profile changer.
-          Drupal.settings.linkit.currentInstance.suppressProfileChanger = true;
-
-          // Create the modal.
-          Drupal.linkit.createModal();
-
-         return false;
+(function($, behavior) {
+  'use strict';
+
+  Drupal.behaviors[behavior] = {
+    attach: function(context, settings) {
+      // If there is no fields, just stop here.
+      if (undefined === settings.linkit || null === settings.linkit.fields) {
+        return false;
+      }
+
+      $.each(settings.linkit.fields, function(i, instance) {
+        $('#' + instance.source, context).once(behavior, function() {
+          var element = this;
+
+          $('.linkit-field-' + instance.source).click(function(event) {
+            event.preventDefault();
+
+            // Only care about selection if the element is a textarea.
+            if ('textarea' === element.nodeName.toLowerCase()) {
+              instance.selection = Drupal.linkit.getDialogHelper('field').getSelection(element);
+            }
+
+            Drupal.settings.linkit.currentInstance = instance;
+            Drupal.linkit.createModal();
+          });
         });
         });
       });
       });
-    });
-  }
-};
-
-/**
- * Linkit field dialog helper.
- */
-Drupal.linkit.registerDialogHelper('field', {
-  init : function() {},
-  afterInit : function () {},
-
-  /**
-   * Insert the link into the field.
-   *
-   * @param {Object} link
-   *   The link object.
-   */
-  insertLink : function(data) {
-    var source = $('#' + Drupal.settings.linkit.currentInstance.source),
-      field_settings = Drupal.settings.linkit.fields[Drupal.settings.linkit.currentInstance.source],
-
-    // Call the insert plugin.
-    link = Drupal.linkit.getInsertPlugin(field_settings.insert_plugin).insert(data, field_settings);
-
-    if (typeof Drupal.settings.linkit.currentInstance.selection != 'undefined') {
-      // Replace the selection and insert the link there.
-      this.replaceSelection(source.get(0), Drupal.settings.linkit.currentInstance.selection, link);
     }
     }
-    else {
-      // Replace the field value.
-      this.replaceFieldValue(source.get(0), link);
-    }
-
-    // Link field can have a title field. If they have, we populate the title
-    // field with the search result title if any.
-    if (typeof field_settings.title_field != 'undefined' && typeof Drupal.settings.linkit.currentInstance.linkContent != 'undefined') {
-      this.replaceFieldValue($('#' + field_settings.title_field).get(0), Drupal.settings.linkit.currentInstance.linkContent);
-    }
-  },
+  };
 
 
   /**
   /**
-   * Get field selection.
+   * Linkit field dialog helper.
    */
    */
-  getSelection : function(e) {
-    // Mozilla and DOM 3.0.
-    if ('selectionStart' in e) {
-        var l = e.selectionEnd - e.selectionStart;
-        return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) };
-    }
-    // IE.
-    else if(document.selection) {
-        e.focus();
-        var r = document.selection.createRange(),
-          tr = e.createTextRange(),
-          tr2 = tr.duplicate();
-        tr2.moveToBookmark(r.getBookmark());
-        tr.setEndPoint('EndToStart',tr2);
-
-        if (r == null || tr == null) {
-          return { start: e.value.length, end: e.value.length, length: 0, text: '' };
+  Drupal.linkit.registerDialogHelper('field', {
+    afterInit: function() {},
+
+    /**
+     * Insert the link into the field.
+     *
+     * @param {Object} data
+     *   The link object.
+     */
+    insertLink: function(data) {
+      var instance = Drupal.settings.linkit.currentInstance,
+          // Call the insert plugin.
+          link = Drupal.linkit.getInsertPlugin(instance.insertPlugin).insert(data, instance);
+
+      if (instance.hasOwnProperty('selection')) {
+        // Replace the selection and insert the link there.
+        this.replaceSelection(instance.source, instance.selection, link);
+      }
+      else if (instance.hasOwnProperty('titleField')) {
+        // The "linkContent" property will always be present when AJAX used.
+        // Otherwise, if you use simple insert without autocomplete, then this
+        // property will be undefined and title field should not be filled in.
+        //
+        // @see Drupal.behaviors.linkitSearch.attach
+        if (instance.hasOwnProperty('linkContent')) {
+          this.replaceFieldValue(instance.titleField, instance.linkContent);
+        }
+
+        // The "path" property will always be present after dialog was
+        // opened and contain raw URL.
+        //
+        // @see Drupal.behaviors.linkitDashboard.attach
+        this.replaceFieldValue(instance.source, data.path);
+      }
+      else {
+        // Replace the field value.
+        this.replaceFieldValue(instance.source, link);
+      }
+    },
+
+    /**
+     * Get field selection.
+     */
+    getSelection: function(element) {
+      var object = {
+        start: element.value.length,
+        end: element.value.length,
+        length: 0,
+        text: ''
+      };
+
+      // Mozilla and DOM 3.0.
+      if ('selectionStart' in element) {
+        var length = element.selectionEnd - element.selectionStart;
+
+        object = {
+          start: element.selectionStart,
+          end: element.selectionEnd,
+          length: length,
+          text: element.value.substr(element.selectionStart, length)
+        };
+      }
+      // IE.
+      else if (document.selection) {
+        element.focus();
+
+        var range = document.selection.createRange(),
+            textRange = element.createTextRange(),
+            textRangeDuplicate = textRange.duplicate();
+
+        textRangeDuplicate.moveToBookmark(range.getBookmark());
+        textRange.setEndPoint('EndToStart', textRangeDuplicate);
+
+        if (!(range || textRange)) {
+          return object;
         }
         }
 
 
         // For some reason IE doesn't always count the \n and \r in the length.
         // For some reason IE doesn't always count the \n and \r in the length.
-        var text_part = r.text.replace(/[\r\n]/g,'.'),
-          text_whole = e.value.replace(/[\r\n]/g,'.'),
-          the_start = text_whole.indexOf(text_part, tr.text.length);
-        return { start: the_start, end: the_start + text_part.length, length: text_part.length, text: r.text };
+        var text_part = range.text.replace(/[\r\n]/g, '.'),
+            text_whole = element.value.replace(/[\r\n]/g, '.'),
+            the_start = text_whole.indexOf(text_part, textRange.text.length);
+
+        object = {
+          start: the_start,
+          end: the_start + text_part.length,
+          length: text_part.length,
+          text: range.text
+        };
+      }
+
+      return object;
+    },
+
+    /**
+     * Replace the field selection.
+     */
+    replaceSelection: function(id, selection, text) {
+      var field = this.getField(id);
+      field.value = field.value.substr(0, selection.start) + text + field.value.substr(selection.end, field.value.length);
+    },
+
+    /**
+     * Replace the field value.
+     */
+    replaceFieldValue: function(id, text) {
+      this.getField(id).value = text;
+    },
+
+    getField: function(id) {
+      return document.getElementById(id);
     }
     }
-    // Browser not supported.
-    else {
-      return { start: e.value.length, end: e.value.length, length: 0, text: '' };
-    }
-  },
-
-   /**
-   * Replace the field selection.
-   */
-  replaceSelection : function (e, selection, text) {
-    var start_pos = selection.start;
-    var end_pos = start_pos + text.length;
-    e.value = e.value.substr(0, start_pos) + text + e.value.substr(selection.end, e.value.length);
-  },
-
-   /**
-   * Replace the field value.
-   */
-  replaceFieldValue : function (e, text) {
-    e.value = text;
-  }
-});
-
-})(jQuery);
+  });
+})(jQuery, 'linkitField');

+ 37 - 1
sites/all/modules/contrib/admin/linkit/js/linkit.js

@@ -6,11 +6,15 @@
 (function ($) {
 (function ($) {
 
 
 // Create the Linkit namespaces.
 // Create the Linkit namespaces.
-Drupal.linkit = Drupal.linkit || {};
+Drupal.linkit = Drupal.linkit || { 'excludeIdSelectors': {} };
 Drupal.linkit.currentInstance = Drupal.linkit.currentInstance || {};
 Drupal.linkit.currentInstance = Drupal.linkit.currentInstance || {};
 Drupal.linkit.dialogHelper = Drupal.linkit.dialogHelper || {};
 Drupal.linkit.dialogHelper = Drupal.linkit.dialogHelper || {};
 Drupal.linkit.insertPlugins = Drupal.linkit.insertPlugins || {};
 Drupal.linkit.insertPlugins = Drupal.linkit.insertPlugins || {};
 
 
+// Exclude ids from ajax_html_ids during AJAX requests.
+Drupal.linkit.excludeIdSelectors.ckeditor = ['[id^="cke_"]'];
+Drupal.linkit.excludeIdSelectors.tokens = ['[id^="token-"]'];
+
 /**
 /**
  * Create the modal dialog.
  * Create the modal dialog.
  */
  */
@@ -140,6 +144,12 @@ Drupal.linkit.registerDialogHelper = function(name, helper) {
 
 
 /**
 /**
  * Get a dialog helper.
  * Get a dialog helper.
+ *
+ * @param {String} name
+ *   The name of helper.
+ *
+ * @return {Object}
+ *   Dialog helper object.
  */
  */
 Drupal.linkit.getDialogHelper = function(name) {
 Drupal.linkit.getDialogHelper = function(name) {
   return Drupal.linkit.dialogHelper[name];
   return Drupal.linkit.dialogHelper[name];
@@ -159,4 +169,30 @@ Drupal.linkit.getInsertPlugin = function(name) {
   return Drupal.linkit.insertPlugins[name];
   return Drupal.linkit.insertPlugins[name];
 };
 };
 
 
+var oldBeforeSerialize = (Drupal.ajax ? Drupal.ajax.prototype.beforeSerialize : false);
+if (oldBeforeSerialize) {
+  /**
+   * Filter the ajax_html_ids list sent in AJAX requests.
+   *
+   * This avoids hitting like max_input_vars, which defaults to 1000,
+   * even with just a few active editor instances.
+   */
+  Drupal.ajax.prototype.beforeSerialize = function (element, options) {
+    var ret = oldBeforeSerialize.call(this, element, options);
+    var excludeSelectors = [];
+    $.each(Drupal.linkit.excludeIdSelectors, function () {
+      if ($.isArray(this)) {
+        excludeSelectors = excludeSelectors.concat(this);
+      }
+    });
+    if (excludeSelectors.length > 0) {
+      options.data['ajax_html_ids[]'] = [];
+      $('[id]:not(' + excludeSelectors.join(',') + ')').each(function () {
+        options.data['ajax_html_ids[]'].push(this.id);
+      });
+    }
+    return ret;
+  }
+}
+
 })(jQuery);
 })(jQuery);

+ 20 - 1
sites/all/modules/contrib/admin/linkit/linkit.api.php

@@ -29,4 +29,23 @@ function hook_linkit_search_plugin_entities_alter(&$plugins) {
     );
     );
     $plugins['my_custom_plugin']['handler'] = $handler;
     $plugins['my_custom_plugin']['handler'] = $handler;
   }
   }
-}
+}
+
+/**
+ * Implements hook_linkit_local_hosts_alter().
+ *
+ * The default behavior is that only the current host is considered "local",
+ * when deciding how to classify a URL. For example, if the user is on
+ * http://www.example.com, then all URLs to other hosts will not be considered
+ * for local URLs. This means that if your users edit content on a different host
+ * from the actual public-facing site, such as https://staging.example.com,
+ * then if they paste in URLs from the public site (www), none of those
+ * URLs will be considered local.
+ *
+ * Implementing this hook will allow you to alter the list (indexed array) of
+ * hosts that will be considered for internal links. Include the protocol
+ * (eg, http or https).
+ */
+function hook_linkit_local_hosts_alter(&$local_hosts) {
+  $local_hosts[] = 'http://www.example.com';
+}

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