summaryrefslogtreecommitdiff
path: root/sites/all/modules/ctools/page_manager
diff options
context:
space:
mode:
authorCtibor Brančík <ctibor@brancik.cz>2016-03-20 19:27:01 +0100
committerCtibor Brančík <ctibor@brancik.cz>2016-03-20 19:27:01 +0100
commit29a6913890a675ddf1a9239b4407f105e02dc95d (patch)
treedd9ba21b73e9e704952b49d5153616a9dfa9b98f /sites/all/modules/ctools/page_manager
parent5ddacae6306ce071d4f7e4d438960d6d3a4c6bd8 (diff)
downloadbrdo-29a6913890a675ddf1a9239b4407f105e02dc95d.tar.gz
brdo-29a6913890a675ddf1a9239b4407f105e02dc95d.tar.bz2
Added drupal modules for site
Diffstat (limited to 'sites/all/modules/ctools/page_manager')
-rw-r--r--sites/all/modules/ctools/page_manager/css/page-manager.css372
-rw-r--r--sites/all/modules/ctools/page_manager/help/about.html11
-rw-r--r--sites/all/modules/ctools/page_manager/help/api-task-handler.html43
-rw-r--r--sites/all/modules/ctools/page_manager/help/api-task-type.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/api-task.html38
-rw-r--r--sites/all/modules/ctools/page_manager/help/custom-pages-access.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/custom-pages-arguments.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/custom-pages-menu.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/custom-pages.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started-create.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started-custom-nodes.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started-custom-vocabulary.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started-members.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started-page-list.html2
-rw-r--r--sites/all/modules/ctools/page_manager/help/getting-started.html15
-rw-r--r--sites/all/modules/ctools/page_manager/help/page-task-type.html4
-rw-r--r--sites/all/modules/ctools/page_manager/help/page_manager.help.ini59
-rw-r--r--sites/all/modules/ctools/page_manager/help/variants.html2
-rw-r--r--sites/all/modules/ctools/page_manager/images/arrow-active.pngbin0 -> 313 bytes
-rw-r--r--sites/all/modules/ctools/page_manager/images/locked-other.pngbin0 -> 262 bytes
-rw-r--r--sites/all/modules/ctools/page_manager/images/locked.pngbin0 -> 273 bytes
-rw-r--r--sites/all/modules/ctools/page_manager/js/page-list.js44
-rw-r--r--sites/all/modules/ctools/page_manager/page_manager.admin.inc1904
-rw-r--r--sites/all/modules/ctools/page_manager/page_manager.api.php39
-rw-r--r--sites/all/modules/ctools/page_manager/page_manager.info13
-rw-r--r--sites/all/modules/ctools/page_manager/page_manager.install204
-rw-r--r--sites/all/modules/ctools/page_manager/page_manager.module1349
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/cache/page_manager_context.inc70
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/task_handlers/http_response.inc332
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/blog.inc121
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/blog_user.inc152
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/comment_reply.inc162
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/contact_site.inc129
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/contact_user.inc155
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/node_edit.inc185
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/node_view.inc166
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/page.admin.inc1521
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/page.inc787
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/poll.inc121
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/search.inc249
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc377
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/user_edit.inc187
-rw-r--r--sites/all/modules/ctools/page_manager/plugins/tasks/user_view.inc161
-rw-r--r--sites/all/modules/ctools/page_manager/theme/page-manager-edit-page.tpl.php53
-rw-r--r--sites/all/modules/ctools/page_manager/theme/page_manager.theme.inc118
45 files changed, 9163 insertions, 0 deletions
diff --git a/sites/all/modules/ctools/page_manager/css/page-manager.css b/sites/all/modules/ctools/page_manager/css/page-manager.css
new file mode 100644
index 000000000..1a7dd5e4b
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/css/page-manager.css
@@ -0,0 +1,372 @@
+body form#page-manager-list-pages-form {
+ margin: 0 0 20px 0;
+}
+
+#page-manager-list-pages-form .form-item {
+ padding-right: 1em; /* LTR */
+ float: left; /* LTR */
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#page-manager-list-pages {
+ width: 100%;
+}
+
+#edit-order-wrapper {
+ clear: left; /* LTR */
+}
+
+#edit-pages-apply,
+#edit-pages-reset {
+ margin-top: 1.65em;
+ float: left; /* LTR */
+}
+
+#page-manager-edit {
+ margin-top: 1em;
+}
+
+#page-manager-edit table {
+ width: 100%;
+}
+
+#page-manager-edit table tr.even {
+ background-color: #fafafa;
+}
+
+#page-manager-list-pages tr.page-manager-disabled td {
+ color: #999;
+}
+
+#page-manager-list-pages tr.page-manager-locked td.page-manager-page-name {
+ font-style: italic;
+ color: green;
+ padding-left: 22px; /* LTR */
+ background: url(../images/locked.png) no-repeat scroll left center; /* LTR */
+}
+
+#page-manager-list-pages tr.page-manager-locked-other td.page-manager-page-name {
+ font-style: italic;
+ color: red;
+ padding-left: 22px; /* LTR */
+ background: url(../images/locked-other.png) no-repeat scroll left center; /* LTR */
+}
+
+/* Force the background color to inherit so that the dropbuttons do not need
+ a specific background color. */
+#page-manager-list-pages td.page-manager-page-operations {
+ vertical-align: top;
+ position: relative;
+ text-align: right;
+}
+
+#page-manager-list-pages td.page-manager-page-operations .ctools-dropbutton {
+ text-align: left; /* LTR */
+}
+
+
+
+#page-manager-edit .page-manager-wrapper {
+ margin: 0;
+ padding: 0 0 0 149px;
+ color: #494949;
+}
+
+#page-manager-edit .page-manager-tabs {
+ border: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations {
+ float: left; /* LTR */
+ width: 150px;
+ margin-left: -150px;
+ margin-top: -1px;
+ height: 100%;
+ font-size: 90%;
+}
+
+#page-manager-edit .page-manager-edit-operations .inside {
+ border-top: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations ul {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 0;
+ margin-left: 0;
+}
+
+#page-manager-edit .page-manager-edit-operations li {
+ list-style: none;
+ background: #F6F6F6;
+ border-top: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ padding: 0 0 0 0;
+ margin: 0;
+ line-height: 2em;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active,
+#page-manager-edit .page-manager-edit-operations li.active-group .page-manager-group-title {
+ background: #FFFFFF url(../images/arrow-active.png) no-repeat scroll right center;
+}
+
+#page-manager-edit .page-manager-edit-operations li.changed,
+#page-manager-edit .page-manager-edit-operations li.changed-group .page-manager-group-title {
+ background-color: #ffe;
+ font-weight: bold;
+}
+
+/** provide a reset for non active stray paths */
+#page-manager-edit .page-manager-edit-operations li.active-group li.not-active .page-manager-group-title,
+#page-manager-edit .page-manager-edit-operations li.changed-group li.not-changed .page-manager-group-title {
+ background: #F6F6F6;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active {
+ border-right: 1px solid white;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active a,
+#page-manager-edit .page-manager-edit-operations li.active a:hover {
+ background: #FFFFFF url(../images/arrow-active.png) no-repeat scroll right center;
+ color: #000000;
+ font-weight: bold;
+}
+
+#page-manager-edit .page-manager-edit-operations li.operation-first {
+ border-top: none;
+}
+
+#page-manager-edit .page-manager-edit-operations li li.operation-first {
+ border-top: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations li a {
+ display: block;
+ padding: 0 0 0 .5em;
+ color: #494949;
+}
+
+#page-manager-edit .page-manager-edit-operations li a:hover {
+ background-color: #eee;
+ text-decoration: none;
+}
+
+#page-manager-edit .ctools-collapsible-container {
+ display: inline-block;
+ position: relative;
+ *float: left; /* LTR */
+ width: 100%;
+}
+
+#page-manager-edit .page-manager-edit-operations li .ctools-collapsible-handle:hover {
+ background-color: #eee;
+}
+
+#page-manager-edit .page-manager-edit-operations li li {
+ border-right: none;
+ border-left: none;
+ margin-left: 1em;
+}
+
+#page-manager-edit .page-manager-edit-operations li ul {
+}
+
+#page-manager-edit .page-manager-group-title {
+ line-height: 2em;
+ font-weight: bold;
+ padding: 0 0 0 .5em;
+}
+
+/* Change the position of the arrow on the dropdown to look nicer with our defaults. */
+#page-manager-edit .page-manager-edit-operations .ctools-toggle {
+ background-position: 0 9px;
+ width: 10px;
+}
+
+#page-manager-edit .page-manager-ajax-pad {
+ float: left; /* LTR */
+ width: 100%;
+ border-left: none;
+ height: 100%;
+ background: white;
+}
+
+/** A riser to force the ajax pad to a minimum height. **/
+#page-manager-edit .page-manager-ajax-pad .page-manager-riser {
+ width: 1px;
+ float: right; /* LTR */
+ height: 400px;
+}
+
+#page-manager-edit .page-manager-ajax-pad .page-manager-riser span {
+ display: none;
+}
+
+#page-manager-edit .page-manager-ajax-pad .content-title {
+ font-weight: bold;
+ font-size: 120%;
+ background-color: #fafafa;
+ border-bottom: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ margin-left: -1px;
+ padding: 2px 5px 2px 20px;
+}
+
+#page-manager-edit .actions {
+ padding: 0 0 0 20px;
+}
+
+#page-manager-edit .primary-actions li {
+ border-top: 1px solid #aaa;
+}
+
+#page-manager-edit .secondary-actions {
+ border-bottom: 1px solid #aaa;
+}
+
+#page-manager-edit .handler-actions {
+ float: right; /* LTR */
+}
+
+#page-manager-edit .actions .page-manager-group-title {
+ float: left; /* LTR */
+ padding-left: 0;
+}
+
+#page-manager-edit .actions ul {
+ float: right; /* LTR */
+ text-align: right; /* LTR */
+ padding: 0;
+ margin: 0;
+ border-right: 1px solid #aaa;
+}
+
+#page-manager-edit .handler-title .actions ul {
+ border-right: none;
+}
+
+#page-manager-edit .actions li {
+ float: left; /* LTR */
+ background: none;
+ list-style: none;
+ border-left: 1px solid #aaa;
+ margin: 0;
+ padding: 0;
+}
+
+#page-manager-edit .actions ul li.operation-last {
+ border-right: none;
+}
+
+#page-manager-edit .actions li a:hover {
+ background-color: #eee;
+ text-decoration: none;
+}
+
+#page-manager-edit .actions li a {
+ display: block;
+ padding: 0.2em 0.5em;
+ color:#0062A0;
+ background-color: #F6F6F6;
+}
+
+#page-manager-edit .page-manager-changed {
+ float: right; /* LTR */
+ font-style: italic;
+ color: #f93;
+ padding-left: 1em;
+ padding-right: 22px;
+ background: url(../images/locked.png) no-repeat scroll right center;
+}
+
+#page-manager-edit .page-manager-ajax-pad .content-content {
+ padding: .5em 20px;
+}
+
+#page-manager-edit .page-manager-ajax-pad textarea {
+ width: 100%;
+}
+
+#page-manager-edit .changed-notification {
+ border: 1px solid #aaa;
+ background-color: #ffe;
+ color: #494949;
+ padding: 1em;
+ margin-top: 1em;
+}
+
+#page-manager-edit .ctools-locked {
+ margin-bottom: 2em;
+}
+
+#page-manager-page-summary .title {
+ font-weight: bold;
+ font-size: 160%;
+}
+
+#page-manager-page-summary .handler-summary {
+}
+
+#page-manager-page-summary .page-summary-operation {
+ text-align: right;
+}
+
+#page-manager-page-summary .page-summary-label {
+ width: 8em;
+ font-weight: bold;
+}
+
+
+.handler-summary dl {
+ margin: 0;
+}
+
+.handler-summary dt {
+ margin: 0;
+ padding: 0;
+}
+
+.handler-summary dd {
+ margin: 0;
+}
+
+.handler-summary ol {
+ margin: 0;
+}
+
+.handler-summary .handler-title {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ background: #fafafa;
+ padding: 0 0 0 5px;
+ margin-top: .5em;
+}
+
+.handler-summary .handler-title .title-label {
+ font-weight: bold;
+ font-size: 120%;
+ line-height: 1.75em;
+}
+
+
+/** Override that obnoxious float on throbber. **/
+#page-manager-edit .progress-disabled {
+ float: none;
+}
+
+#page-manager-edit .progress-disabled + .ajax-progress {
+ float: right;
+ position: relative;
+ top: -2em;
+}
+
+#page-manager-list-pages-form .progress-disabled + .ajax-progress {
+ position: relative;
+ top: 2em;
+ left: -.5em;
+}
diff --git a/sites/all/modules/ctools/page_manager/help/about.html b/sites/all/modules/ctools/page_manager/help/about.html
new file mode 100644
index 000000000..fa58acafb
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/about.html
@@ -0,0 +1,11 @@
+The Page Manager module creates and manages pages in your Drupal site. Pages are defined as items that have a path and provide output to the user. It is a complete round trip from getting user input to providing user output.
+
+There are two types of pages that the Page Manager currently supports:
+<dl>
+<dt>Custom pages</dt>
+<dd>Custom pages are defined completely by the administrator. Their path, access control and visible menu characteristics are completely arbitrary.</dd>
+<dt>System pages</dt>
+<dd>System pages are defined by Drupal and Drupal modules. They primarily override pre-existing pages to provide different functionality. They often do not allow such features as access control in favor of what already exists, and they will usually 'fall back' to default Drupal behavior.
+</dl>
+
+Both types of pages figure out what to show the user by using <strong>Variants</strong>. Variants are output handlers, and every page should have at least one. Most pages will simply have only one. Pages with multiple variants will choose one and only one Variant to display content to the user and will use the <strong>Selection Rules</strong> to figure out which Variant to display. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/api-task-handler.html b/sites/all/modules/ctools/page_manager/help/api-task-handler.html
new file mode 100644
index 000000000..4544a2a86
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/api-task-handler.html
@@ -0,0 +1,43 @@
+task handler definition:
+ title -- visible title of the task handler.
+ description -- description of the task handler.
+ task type -- The type of the task this handler can service.
+ render -- callback of the function to render the handler. The arguments to this callback are specific to the task type.
+ admin title -- callback to render the admin title as this handler is listed.
+ params: $handler, $task, $subtask_id
+ admin summary -- callback to render what's in the collapsible info as the handler is listed. Optional.
+ params: $handler, $task, $subtask_id
+ default conf -- either an array() of default conf data or a callback that returns an array.
+ params: $handler, $task, $subtask_id
+ save -- callback to call just prior to the task handler being saved so it can adjust its data.
+ params: &$handler, $update (as drupal_write_record would receive)
+ export -- callback to call just prior to the task being exported. It should return text to append to the export if necessary.
+ params: &$handler, $indent
+
+ forms => array(
+ 'id' => array(
+ 'form' => form callback (receives $form, $form_state)
+ 'submit' => submit callback
+ 'validate' => validate callback
+ 'include' => an optional file to include to get functionality for this form. Must include full path.
+ 'no return' => hide the 'return' button, meaning that the form requires extra steps if submitted
+ 'alternate next' => an alternate next form. Used for hidden edit forms that don't have tabs.
+ 'no blocks' => if TRUE, use Drupal's mechanism to not render blocks for this form.
+ )
+ )
+ ),
+
+ 'add forms' => array(
+ 'form1', => t('form title'),
+ 'form2', => t('form title'),
+ // ...etc.../
+),
+ 'edit forms' => array(
+ 'id' => t('tab name'),
+ 'id2' => t('tab name'),
+ ),
+
+ If a form name is blank it is a 'hidden' form -- it has no tab but can still be reached.
+
+
+Notes: Because #required validation cannot be skipped when clicking cancel, please don't use it. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/api-task-type.html b/sites/all/modules/ctools/page_manager/help/api-task-type.html
new file mode 100644
index 000000000..eb87265f6
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/api-task-type.html
@@ -0,0 +1,2 @@
+
+defines a task type, grouping tasks together and providing a common UI for them. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/api-task.html b/sites/all/modules/ctools/page_manager/help/api-task.html
new file mode 100644
index 000000000..cd6e3d0cb
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/api-task.html
@@ -0,0 +1,38 @@
+task definition:
+ title -- visible title of the task.
+ description -- description of the task.
+ hook menu -- function to delegate from hook_menu. Params: &$items, $task
+ hook menu alter -- function to delegate from hook_menu_alter. Params: &$items, $task
+ hook theme -- function to delegate from hook_theme. Params: &$items, $task
+
+ admin name -- if set an admin menu will appear in the delegator UI
+ admin description -- to describe the admin menu
+
+ admin access callback -- if set, the callback to use to determine administrative
+ access to this task. Defaults to user_access. Note that this is required even
+ if delegator isn't handling administration, since this gets used to on handler
+ edit forms.
+ admin access arguments -- If set, the arguments to use to determine administrative
+ access to this task. Defaults to array('administer delegator');
+
+ type -- The type of the task, used to determine which handlers can service it.
+
+ subtasks -- can be TRUE in which case it supports subtasks with the default
+ configuration or a string (array?) with callbacks to fetch subtask data.
+ subtask callback -- A callback which returns just one subtask. Param: $task, $subtask_id
+ subtasks callback -- A callback which returns an array of all subtasks.
+ This MUST return an array, even if it's empty.Param: $task
+
+ default handlers -- If the task contains any default handlers, they can be included here.
+
+task names must not contain a - as that is used to separate the task name from the subtask ID.
+
+subtasks implement data very similar to their parent task. In particular, they
+implement the following items exactly like their task:
+ hook menu
+ hook menu alter
+ description
+ admin name
+ admin description
+ admin access callback
+ admin access arguments
diff --git a/sites/all/modules/ctools/page_manager/help/custom-pages-access.html b/sites/all/modules/ctools/page_manager/help/custom-pages-access.html
new file mode 100644
index 000000000..a2643c28c
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/custom-pages-access.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528072">http://drupal.org/node/528072</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/custom-pages-arguments.html b/sites/all/modules/ctools/page_manager/help/custom-pages-arguments.html
new file mode 100644
index 000000000..516a4292b
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/custom-pages-arguments.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528058">http://drupal.org/node/528058</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/custom-pages-menu.html b/sites/all/modules/ctools/page_manager/help/custom-pages-menu.html
new file mode 100644
index 000000000..48cf9c397
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/custom-pages-menu.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528078">http://drupal.org/node/528078</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/custom-pages.html b/sites/all/modules/ctools/page_manager/help/custom-pages.html
new file mode 100644
index 000000000..18e66d4be
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/custom-pages.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528050">http://drupal.org/node/528050</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started-create.html b/sites/all/modules/ctools/page_manager/help/getting-started-create.html
new file mode 100644
index 000000000..a3d295e38
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started-create.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528038">http://drupal.org/node/528038</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started-custom-nodes.html b/sites/all/modules/ctools/page_manager/help/getting-started-custom-nodes.html
new file mode 100644
index 000000000..d62eb0f32
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started-custom-nodes.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528044">http://drupal.org/node/528044</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started-custom-vocabulary.html b/sites/all/modules/ctools/page_manager/help/getting-started-custom-vocabulary.html
new file mode 100644
index 000000000..7148cd96a
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started-custom-vocabulary.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528046">http://drupal.org/node/528046</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started-members.html b/sites/all/modules/ctools/page_manager/help/getting-started-members.html
new file mode 100644
index 000000000..87b21227c
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started-members.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528040">http://drupal.org/node/528040</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started-page-list.html b/sites/all/modules/ctools/page_manager/help/getting-started-page-list.html
new file mode 100644
index 000000000..d60bea4ad
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started-page-list.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528036">http://drupal.org/node/528036</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/getting-started.html b/sites/all/modules/ctools/page_manager/help/getting-started.html
new file mode 100644
index 000000000..4e4f24ae8
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/getting-started.html
@@ -0,0 +1,15 @@
+
+Note: this page is currently very preliminary. Please visit <a href="http://drupal.org/node/528034">http://drupal.org/node/528034</a> to help provide this documentation page!
+
+This is a quick summary:
+
+<ul>
+<li>Visit administer >> site building >> pages to get to the primary page manager interface.</li>
+<li>You can add custom pages for your basic landing pages, front pages, whatever you like for normal content display.</li>
+<li>You can use the system pages to create finer control of how taxonomy vocabularies, nodes and user profiles are displayed.</li>
+<li>When you add your first custom page, do not bother with the optional features. You will not need these until you get to more advanced tasks.</li>
+<li>The selection rules are the key to creating node displays for just one node type.</li>
+<li>Everything in this system is pluggable. A little PHP knowledge and exploration of the plugins directories can take you a long way.</li>
+</ul>
+
+
diff --git a/sites/all/modules/ctools/page_manager/help/page-task-type.html b/sites/all/modules/ctools/page_manager/help/page-task-type.html
new file mode 100644
index 000000000..c382c76ff
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/page-task-type.html
@@ -0,0 +1,4 @@
+
+Additional 'task' keys support:
+
+operations -- a list of operations suitable for theme('links') \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/help/page_manager.help.ini b/sites/all/modules/ctools/page_manager/help/page_manager.help.ini
new file mode 100644
index 000000000..05cadb482
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/page_manager.help.ini
@@ -0,0 +1,59 @@
+[advanced help settings]
+line break = TRUE
+
+[about]
+title = About Page Manager
+weight = -100
+
+[getting-started]
+title = Getting Started
+weight = -80
+
+[getting-started-page-list]
+title = The page list
+weight = -90
+parent = getting-started
+
+[getting-started-create]
+title = Creating a page
+weight = -80
+parent = getting-started
+
+[getting-started-members]
+title = Tutorial: Make a page that looks different for members
+weight = -70
+parent = getting-started
+
+[getting-started-custom-nodes]
+title = Tutorial: Customize the look of a single node type
+weight = -60
+parent = getting-started
+
+[getting-started-custom-vocabulary]
+title = Tutorial: Customize the look of a single taxonomy vocabulary
+weight = -50
+parent = getting-started
+
+[custom-pages]
+title = Custom pages
+weight = -50
+
+[custom-pages-arguments]
+title = Arguments
+weight = -100
+parent = custom-pages
+
+[custom-pages-access]
+title = Access control
+weight = -90
+parent = custom-pages
+
+[custom-pages-menu]
+title = Menu items
+weight = -80
+parent = custom-pages
+
+[variants]
+title = Variants
+weight = -40
+
diff --git a/sites/all/modules/ctools/page_manager/help/variants.html b/sites/all/modules/ctools/page_manager/help/variants.html
new file mode 100644
index 000000000..48cf9c397
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/help/variants.html
@@ -0,0 +1,2 @@
+
+Please visit <a href="http://drupal.org/node/528078">http://drupal.org/node/528078</a> to help provide this documentation page. \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/images/arrow-active.png b/sites/all/modules/ctools/page_manager/images/arrow-active.png
new file mode 100644
index 000000000..3bbd3c27f
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/images/arrow-active.png
Binary files differ
diff --git a/sites/all/modules/ctools/page_manager/images/locked-other.png b/sites/all/modules/ctools/page_manager/images/locked-other.png
new file mode 100644
index 000000000..b84a154e8
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/images/locked-other.png
Binary files differ
diff --git a/sites/all/modules/ctools/page_manager/images/locked.png b/sites/all/modules/ctools/page_manager/images/locked.png
new file mode 100644
index 000000000..2116eb1c8
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/images/locked.png
Binary files differ
diff --git a/sites/all/modules/ctools/page_manager/js/page-list.js b/sites/all/modules/ctools/page_manager/js/page-list.js
new file mode 100644
index 000000000..16b52911d
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/js/page-list.js
@@ -0,0 +1,44 @@
+
+/**
+ * Provide some extra responses for the page list so we can have automatic
+ * on change.
+ */
+
+Drupal.behaviors.PageManagerList = function() {
+ var timeoutID = 0;
+ $('form#page-manager-list-pages-form select:not(.pm-processed)')
+ .addClass('pm-processed')
+ .change(function() {
+ $('#edit-pages-apply').click();
+ });
+ $('form#page-manager-list-pages-form input[type=text]:not(.pm-processed)')
+ .addClass('pm-processed')
+ .keyup(function(e) {
+ switch (e.keyCode) {
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ case 20: // caps lock
+ case 33: // page up
+ case 34: // page down
+ case 35: // end
+ case 36: // home
+ case 37: // left arrow
+ case 38: // up arrow
+ case 39: // right arrow
+ case 40: // down arrow
+ case 9: // tab
+ case 13: // enter
+ case 27: // esc
+ return false;
+ default:
+ if (!$('#edit-pages-apply').hasClass('ctools-ajaxing')) {
+ if ((timeoutID)) {
+ clearTimeout(timeoutID);
+ }
+
+ timeoutID = setTimeout(function() { $('#edit-pages-apply').click(); }, 300);
+ }
+ }
+ });
+}
diff --git a/sites/all/modules/ctools/page_manager/page_manager.admin.inc b/sites/all/modules/ctools/page_manager/page_manager.admin.inc
new file mode 100644
index 000000000..0f164fe50
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/page_manager.admin.inc
@@ -0,0 +1,1904 @@
+<?php
+
+/**
+ * @file
+ * Administrative functions for the page manager.
+ *
+ * This provides the UI to list, create, edit and delete pages, though much
+ * of this is delegated through to individual tasks.
+ */
+
+/**
+ * Output a list of pages that are managed.
+ */
+function page_manager_list_page($js = NULL) {
+ // Prevent this page from showing up when random other links fail.
+ if ($js && $js != 'ajax' && $js != 'nojs') {
+ return MENU_NOT_FOUND;
+ }
+
+ // TRUE if 'ajax', FALSE if otherwise.
+ $js = $js == 'ajax';
+
+ // If we do any form rendering, it's to completely replace a form on the
+ // page, so don't let it force our ids to change.
+ if ($js && isset($_POST['ajax_html_ids'])) {
+ unset($_POST['ajax_html_ids']);
+ }
+
+ if (module_exists('advanced_help') && !$js) {
+ drupal_set_message(theme('advanced_help_topic', array(
+ 'module' => 'page_manager',
+ 'topic' => 'getting-started',
+ 'type' => t('See the getting started guide for more information.'),
+ )));
+ }
+
+ $tasks = page_manager_get_tasks_by_type('page');
+ $pages = array('operations' => array(), 'tasks' => array());
+
+ page_manager_get_pages($tasks, $pages);
+
+ // Add lock icon to all locked tasks.
+ global $user;
+
+ ctools_include('object-cache');
+ $locks = ctools_object_cache_test_objects('page_manager_page', $pages['tasks']);
+ foreach ($locks as $task_name => $lock) {
+ if ($lock->uid == $user->uid) {
+ $pages['rows'][$task_name]['class'][] = ' page-manager-locked';
+ $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by you. Nobody else may edit this page until these changes are saved or canceled.');
+ }
+ else {
+ $pages['rows'][$task_name]['class'][] = ' page-manager-locked-other';
+ $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by another user. You may not edit this page without breaking the lock.');
+ }
+ }
+
+ $input = $_POST;
+
+ // Respond to a reset command by clearing session and doing a drupal goto
+ // back to the base URL.
+ if (isset($input['op']) && $input['op'] == t('Reset')) {
+ unset($_SESSION['page_manager']['#admin']);
+ if (!$js) {
+ drupal_goto($_GET['q']);
+ }
+ // clear everything but form id, form build id and form token:
+ $keys = array_keys($input);
+ foreach ($keys as $id) {
+ if ($id != 'form_id' && $id != 'form_build_id' && $id != 'form_token') {
+ unset($input[$id]);
+ }
+ }
+ $replace_form = TRUE;
+ }
+ if (count($input) <= 1) {
+ if (isset($_SESSION['page_manager']['#admin']) && is_array($_SESSION['page_manager']['#admin'])) {
+ $input = $_SESSION['page_manager']['#admin'];
+ }
+ }
+ else {
+ $_SESSION['page_manager']['#admin'] = $input;
+ unset($_SESSION['page_manager']['#admin']['q']);
+ }
+
+ $form_state = array(
+ 'pages' => &$pages,
+ 'input' => $input,
+ 'rerender' => TRUE,
+ 'no_redirect' => TRUE,
+ );
+
+ // This form will sort and filter the pages.
+ $form = drupal_build_form('page_manager_list_pages_form', $form_state);
+
+ $header = array(
+ array('data' => t('Type'), 'class' => array('page-manager-page-type')),
+ array('data' => t('Module'), 'class' => array('page-manager-page-module')),
+ array('data' => t('Name'), 'class' => array('page-manager-page-name')),
+ array('data' => t('Title'), 'class' => array('page-manager-page-title')),
+ array('data' => t('Path'), 'class' => array('page-manager-page-path')),
+ array('data' => t('Storage'), 'class' => array('page-manager-page-storage')),
+ );
+
+ $header[] = array('data' => t('Operations'), 'class' => array('page-manager-page-operations'));
+ $table = theme('table', array('header' => $header, 'rows' => $pages['rows'], 'attributes' => array('id' => 'page-manager-list-pages')));
+
+ $operations = '<div id="page-manager-links" class="links">' . theme('links', array('links' => $pages['operations'])) . '</div>';
+
+ drupal_add_css(drupal_get_path('module', 'page_manager') . '/css/page-manager.css');
+
+
+ if (!$js) {
+ return array('#markup' => drupal_render($form) . $table . $operations);
+ }
+
+ ctools_include('ajax');
+ $commands = array();
+ $commands[] = ajax_command_replace('#page-manager-list-pages', $table);
+ if (!empty($replace_form)) {
+ $commands[] = ajax_command_replace('#page-manager-list-pages-form', drupal_render($form));
+ }
+ print ajax_render($commands);
+ ajax_footer();
+}
+
+/**
+ * Sort tasks into buckets based upon whether or not they have subtasks.
+ */
+function page_manager_get_pages($tasks, &$pages, $task_id = NULL) {
+ foreach ($tasks as $id => $task) {
+ if (empty($task_id) && !empty($task['page operations'])) {
+ $pages['operations'] = array_merge($pages['operations'], $task['page operations']);
+ }
+
+ // If a type has subtasks, add its subtasks in its own table.
+ if (!empty($task['subtasks'])) {
+ page_manager_get_pages(page_manager_get_task_subtasks($task), $pages, $task['name']);
+ continue;
+ }
+
+ if (isset($task_id)) {
+ $task_name = page_manager_make_task_name($task_id, $task['name']);
+ }
+ else {
+ $task_name = $task['name'];
+ }
+
+ $class = array('page-task-' . $id);
+ if (isset($task['row class'])) {
+ $class[] = $task['row class'];
+ }
+
+ if (!empty($task['disabled'])) {
+ $class[] = 'page-manager-disabled';
+ }
+
+ $path = array();
+ $visible_path = '';
+ if (!empty($task['admin path'])) {
+ foreach (explode('/', $task['admin path']) as $bit) {
+ if (isset($bit[0]) && $bit[0] != '!') {
+ $path[] = $bit;
+ }
+ }
+
+ $path = implode('/', $path);
+ if (empty($task['disabled']) && strpos($path, '%') === FALSE) {
+ $visible_path = l('/' . $task['admin path'], $path);
+ }
+ else {
+ $visible_path = '/' . $task['admin path'];
+ }
+ }
+
+ $row = array('data' => array(), 'class' => $class, 'title' => strip_tags($task['admin description']));
+
+ $type = isset($task['admin type']) ? $task['admin type'] : t('System');
+ if (isset($task['module'])) {
+ $module = $task['module'];
+ }
+ elseif (isset($task['subtask']->export_module)) {
+ $module = $task['subtask']->export_module;
+ }
+ else {
+ $module = '';
+ }
+ $pages['types'][$type] = $type;
+ $row['data']['type'] = array('data' => $type, 'class' => array('page-manager-page-type'));
+ $row['data']['module'] = array('data' => $module, 'class' => array('page-manager-page-module'));
+ $row['data']['name'] = array('data' => $task_name, 'class' => array('page-manager-page-name'));
+ $row['data']['title'] = array('data' => $task['admin title'], 'class' => array('page-manager-page-title'));
+ $row['data']['path'] = array('data' => $visible_path, 'class' => array('page-manager-page-path'));
+
+ $storage = isset($task['storage']) ? $task['storage'] : t('In code');
+ $pages['storages'][$storage] = $storage;
+ $row['data']['storage'] = array('data' => $storage, 'class' => array('page-manager-page-storage'));
+
+
+/*
+ if (empty($task['disabled'])) {
+ $item = menu_get_item($path);
+ if (empty($item)) {
+ dsm($path);
+ }
+ else {
+ dsm($item);
+ }
+ }
+*/
+ $operations = array(
+ array(
+ 'title' => t('Edit'),
+ 'href' => page_manager_edit_url($task_name),
+ ),
+ );
+
+ if (!empty($task['enable callback'])) {
+ if (!empty($task['disabled'])) {
+ array_unshift($operations, array(
+ 'title' => t('Enable'),
+ 'href' => 'admin/structure/pages/nojs/enable/' . $task_name,
+ 'query' => array('token' => drupal_get_token($task_name)),
+ ));
+ }
+ else {
+ $operations[] = array(
+ 'title' => t('Disable'),
+ 'href' => 'admin/structure/pages/nojs/disable/' . $task_name,
+ 'query' => array('token' => drupal_get_token($task_name)),
+ );
+ }
+ }
+
+ $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline'))));
+
+ $row['data']['operations'] = array('data' => $ops, 'class' => array('page-manager-page-operations'));
+
+ $pages['disabled'][$task_name] = !empty($task['disabled']);
+ $pages['tasks'][] = $task_name;
+ $pages['rows'][$task_name] = $row;
+ }
+}
+
+/**
+ * Provide a form for sorting and filtering the list of pages.
+ */
+function page_manager_list_pages_form($form, &$form_state) {
+ // This forces the form to *always* treat as submitted which is
+ // necessary to make it work.
+ if (empty($_POST)) {
+ $form["#programmed"] = TRUE;
+ }
+ $form['#action'] = url('admin/structure/pages/nojs/', array('absolute' => TRUE));
+ if (!variable_get('clean_url', FALSE)) {
+ $form['q'] = array(
+ '#type' => 'hidden',
+ '#value' => $_GET['q'],
+ );
+ }
+
+ $all = array('all' => t('<All>'));
+
+ $form['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Type'),
+ '#options' => $all + $form_state['pages']['types'],
+ '#default_value' => 'all',
+ );
+
+ $form['storage'] = array(
+ '#type' => 'select',
+ '#title' => t('Storage'),
+ '#options' => $all + $form_state['pages']['storages'],
+ '#default_value' => 'all',
+ );
+
+ $form['disabled'] = array(
+ '#type' => 'select',
+ '#title' => t('Enabled'),
+ '#options' => $all + array('0' => t('Enabled'), '1' => t('Disabled')),
+ '#default_value' => 'all',
+ );
+
+ $form['search'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Search'),
+ );
+
+ $form['order'] = array(
+ '#type' => 'select',
+ '#title' => t('Sort by'),
+ '#options' => array(
+ 'disabled' => t('Enabled, title'),
+ 'title' => t('Title'),
+ 'name' => t('Name'),
+ 'path' => t('Path'),
+ 'type' => t('Type'),
+ 'storage' => t('Storage'),
+ ),
+ '#default_value' => 'disabled',
+ );
+
+ $form['sort'] = array(
+ '#type' => 'select',
+ '#title' => t('Order'),
+ '#options' => array(
+ 'asc' => t('Up'),
+ 'desc' => t('Down'),
+ ),
+ '#default_value' => 'asc',
+ );
+
+ $form['submit'] = array(
+ '#name' => '', // so it won't in the $_GET args
+ '#type' => 'submit',
+ '#id' => 'edit-pages-apply',
+ '#value' => t('Apply'),
+ '#attributes' => array('class' => array('use-ajax-submit ctools-auto-submit-click')),
+ );
+
+ $form['reset'] = array(
+ '#type' => 'submit',
+ '#id' => 'edit-pages-reset',
+ '#value' => t('Reset'),
+ '#attributes' => array('class' => array('use-ajax-submit')),
+ );
+
+ $form['#attached']['js'] = array(ctools_attach_js('auto-submit'), ctools_attach_js('page-list', 'page_manager'));
+ $form['#attached']['library'][] = array('system', 'drupal.ajax');
+ $form['#attached']['library'][] = array('system', 'jquery.form');
+ $form['#prefix'] = '<div class="clearfix">';
+ $form['#suffix'] = '</div>';
+ $form['#attributes'] = array('class' => array('ctools-auto-submit-full-form'));
+
+ return $form;
+}
+
+/**
+ * Accept submission from the page manager sort/filter form and apply it
+ * to the list of pages.
+ */
+function page_manager_list_pages_form_submit(&$form, &$form_state) {
+ // Filter and re-sort the pages.
+
+ // This is a copy.
+ $rows = $form_state['pages']['rows'];
+
+ $sorts = array();
+ foreach ($rows as $name => $data) {
+ // Filter
+ if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $data['data']['type']['data']) {
+ continue;
+ }
+
+ if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $data['data']['storage']['data']) {
+ continue;
+ }
+
+ if ($form_state['values']['disabled'] != 'all' && $form_state['values']['disabled'] != $form_state['pages']['disabled'][$name]) {
+ continue;
+ }
+
+ if ($form_state['values']['search'] &&
+ strpos($data['data']['name']['data'], $form_state['values']['search']) === FALSE &&
+ strpos($data['data']['path']['data'], $form_state['values']['search']) === FALSE &&
+ strpos($data['data']['title']['data'], $form_state['values']['search']) === FALSE) {
+ continue;
+ }
+ // Set up sorting
+ switch ($form_state['values']['order']) {
+ case 'disabled':
+ $sorts[$name] = !$form_state['pages']['disabled'][$name] . $data['data']['title']['data'];
+ break;
+ case 'title':
+ $sorts[$name] = $data['data']['title']['data'];
+ break;
+ case 'name':
+ $sorts[$name] = $data['data']['name']['data'];
+ break;
+ case 'path':
+ $sorts[$name] = $data['data']['path']['data'];
+ break;
+ case 'type':
+ $sorts[$name] = $data['data']['type']['data'];
+ break;
+ case 'storage':
+ $sorts[$name] = $data['data']['storage']['data'];
+ break;
+ }
+ }
+
+ // Now actually sort
+ if ($form_state['values']['sort'] == 'desc') {
+ arsort($sorts);
+ }
+ else {
+ asort($sorts);
+ }
+
+ // Nuke the original.
+ $form_state['pages']['rows'] = array();
+ // And restore.
+ foreach ($sorts as $name => $title) {
+ $form_state['pages']['rows'][$name] = $rows[$name];
+ }
+
+}
+
+/**
+ * Render the edit page for a a page, custom or system.
+ */
+function page_manager_edit_page($page) {
+ drupal_set_title($page->subtask['admin title'], PASS_THROUGH);
+ // Provide and process the save page form before anything else.
+ $form_state = array('page' => &$page);
+ $built_form = drupal_build_form('page_manager_save_page_form', $form_state);
+ $form = drupal_render($built_form);
+
+ $operations = page_manager_get_operations($page);
+ $args = array('summary');
+ $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => array('operations-main')), 'nav');
+ $content = page_manager_get_operation_content(FALSE, $page, $args, $operations);
+ $output = theme('page_manager_edit_page', array('page' => $page, 'save' => $form, 'operations' => $rendered_operations, 'content' => $content));
+ return array('#markup' => $output);
+}
+
+/**
+ * Entry point to edit a single operation for a page.
+ *
+ * @param $js
+ * Whether or not the page was called via javascript.
+ * @param $page
+ * The cached page that is being edited.
+ * @param ...
+ * A number of items used to drill down into the actual operation called.
+ */
+function page_manager_edit_page_operation() {
+ $args = func_get_args();
+ $js = array_shift($args);
+ $page = array_shift($args);
+
+ $operations = page_manager_get_operations($page);
+ $content = page_manager_get_operation_content($js, $page, $args, $operations);
+
+ // If the operation requested we go somewhere else afterward, oblige it.
+ if (isset($content['new trail'])) {
+ $args = $content['new trail'];
+ // Get operations again, for the operation may have changed their availability.
+ $operations = page_manager_get_operations($page);
+ $content = page_manager_get_operation_content($js, $page, $args, $operations);
+ }
+
+ // Rendering the content may have been a form submission that changed the
+ // operations, such as renaming or adding a handler. Thus we get a new set
+ // of operations.
+ $operations = page_manager_get_operations($page);
+ $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => array('operations-main')), 'nav');
+
+ // Since this form should never be submitted to this page, process it late so
+ // that we can be sure it notices changes.
+ $form_state = array('page' => &$page);
+ $built_form = drupal_build_form('page_manager_save_page_form', $form_state);
+ $form = drupal_render($built_form);
+
+ $output = theme('page_manager_edit_page', array('page' => $page, 'save' => $form, 'operations' => $rendered_operations, 'content' => $content));
+
+ if ($js) {
+ $commands = array();
+ $commands[] = ajax_command_replace('#page-manager-edit', $output);
+
+ print ajax_render($commands);
+ ajax_footer();
+ return;
+ }
+
+ drupal_set_title($page->subtask['admin title'], PASS_THROUGH);
+ return $output;
+}
+
+/**
+ * Take the operations array from a task and expand it.
+ *
+ * This allows some of the operations to be dynamic, based upon settings
+ * on the task or the task's handlers. Each operation should have a type. In
+ * addition to all the types allowed in page_manager_render_operations, these
+ * types will be dynamically replaced with something else:
+ * - 'handlers': An automatically created group that contains all the task's
+ * handlers and appropriate links.
+ * - 'function': A callback (which will be placed in the 'function' parameter
+ * that should return an array of operations. This can be used to provide
+ * additional, dynamic links if needed.
+ */
+function page_manager_get_operations($page, $operations = NULL) {
+ if (!isset($operations)) {
+ // All tasks have at least these 2 ops:
+ $operations = array(
+ 'summary' => array(
+ 'title' => t('Summary'),
+ 'description' => t('Get a summary of the information about this page.'),
+ 'path' => 'admin/structure/pages/edit',
+ 'ajax' => FALSE,
+ 'no operations' => TRUE,
+ 'form info' => array(
+ 'no buttons' => TRUE,
+ ),
+ 'form' => 'page_manager_page_summary',
+ ),
+ 'actions' => array(
+ 'type' => 'group',
+ 'title' => '',
+ 'class' => array('operations-actions'),
+ 'location' => 'primary',
+ 'children' => array(),
+ ),
+ );
+
+ if (isset($page->subtask['operations'])) {
+ $operations += $page->subtask['operations'];
+ // add actions separately.
+ if (!empty($page->subtask['operations']['actions'])) {
+ $operations['actions']['children'] += $page->subtask['operations']['actions']['children'];
+ }
+ }
+ $operations['handlers'] = array('type' => 'handlers');
+ }
+
+ $result = array();
+ foreach ($operations as $id => $operation) {
+ if (empty($operation['type'])) {
+ $operation['type'] = 'operation';
+ }
+ switch ($operation['type']) {
+ case 'handlers':
+ $result[$id] = page_manager_get_handler_operations($page);
+ break;
+ case 'function':
+ if (function_exists($operation['function'])) {
+ $retval = $function($page, $operation);
+ if (is_array($retval)) {
+ $result[$id] = $retval;
+ }
+ }
+ break;
+ default:
+ $result[$id] = $operation;
+ }
+ }
+
+ if (!empty($page->subtask['enable callback']) && !empty($page->subtask['disabled']) && empty($result['actions']['children']['enable'])) {
+ $result['actions']['children']['enable'] = array(
+ 'title' => t('Enable'),
+ 'description' => t('Activate this page so that it will be in use in your system.'),
+ 'form' => 'page_manager_enable_form',
+ 'ajax' => FALSE,
+ 'silent' => TRUE,
+ 'no update and save' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Enable'),
+ ),
+ );
+ }
+
+ if (!empty($page->subtask['enable callback']) && empty($page->subtask['disabled']) && empty($result['actions']['children']['disable'])) {
+ $result['actions']['children']['disable'] = array(
+ 'title' => t('Disable'),
+ 'description' => t('De-activate this page. The data will remain but the page will not be in use on your system.'),
+ 'form' => 'page_manager_disable_form',
+ 'ajax' => FALSE,
+ 'silent' => TRUE,
+ 'no update and save' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Disable'),
+ ),
+ );
+ }
+
+ $result['actions']['children']['add'] = array(
+ 'title' => t('Add variant'),
+ 'description' => t('Add a new variant to this page.'),
+ 'form' => 'page_manager_handler_add',
+ 'ajax' => FALSE,
+ 'silent' => TRUE, // prevents a message about updating and prevents this item from showing as changed.
+ 'no update and save' => TRUE, // get rid of update and save button which is bad here.
+ 'form info' => array(
+ 'finish text' => t('Create variant'),
+ ),
+ );
+
+ // Restrict variant import due to security implications.
+ if (user_access('use ctools import')) {
+ $result['actions']['children']['import'] = array(
+ 'title' => t('Import variant'),
+ 'description' => t('Add a new variant to this page from code exported from another page.'),
+ 'form' => 'page_manager_handler_import',
+ );
+ }
+
+ if (count($page->handlers) > 1) {
+ $result['actions']['children']['rearrange'] = array(
+ 'title' => t('Reorder variants'),
+ 'ajax' => FALSE,
+ 'description' => t('Change the priority of the variants to ensure that the right one gets selected.'),
+ 'form' => 'page_manager_handler_rearrange',
+ );
+ }
+
+ // This is a special operation used to configure a new task handler before
+ // it is added.
+ if (isset($page->new_handler)) {
+ $plugin = page_manager_get_task_handler($page->new_handler->handler);
+ $result['actions']['children']['configure'] = array(
+ 'title' => t('Configure'),
+ 'description' => t('Configure a newly created variant prior to actually adding it to the page.'),
+ 'ajax' => FALSE,
+ 'no update and save' => TRUE, // get rid of update and save button which is bad here.
+ 'form info' => array(
+ // We use our own cancel and finish callback to handle the fun stuff.
+ 'finish callback' => 'page_manager_handler_add_finish',
+ 'cancel callback' => 'page_manager_handler_add_cancel',
+ 'show trail' => TRUE,
+ 'show back' => TRUE,
+ 'finish text' => t('Create variant'),
+ ),
+ 'form' => array(
+ 'forms' => $plugin['forms'],
+ ),
+ );
+
+ foreach ($page->forms as $id) {
+ if (isset($plugin['add features'][$id])) {
+ $result['actions']['children']['configure']['form']['order'][$id] = $plugin['add features'][$id];
+ }
+ else if (isset($plugin['required forms'][$id])) {
+ $result['actions']['children']['configure']['form']['order'][$id] = $plugin['required forms'][$id];
+ }
+ }
+ }
+
+ if ($page->locked) {
+ $result['actions']['children']['break-lock'] = array(
+ 'title' => t('Break lock'),
+ 'description' => t('Break the lock on this page so that you can edit it.'),
+ 'form' => 'page_manager_break_lock',
+ 'ajax' => FALSE,
+ 'no update and save' => TRUE, // get rid of update and save button which is bad here.
+ 'form info' => array(
+ 'finish text' => t('Break lock'),
+ ),
+ 'even locked' => TRUE, // show button even if locked
+ 'silent' => TRUE,
+ );
+ }
+
+ drupal_alter('page_manager_operations', $result, $page);
+ return $result;
+}
+
+/**
+ * Collect all the operations related to task handlers (variants) and
+ * build a menu.
+ */
+function page_manager_get_handler_operations(&$page) {
+ ctools_include('export');
+ $group = array(
+ 'type' => 'group',
+ 'class' => array('operations-handlers'),
+ 'title' => t('Variants'),
+ );
+
+ $operations = array();
+
+ // If there is only one variant, let's not have it collapsible.
+ $collapsible = count($page->handler_info) != 1;
+ foreach ($page->handler_info as $id => $info) {
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ continue;
+ }
+ $handler = $page->handlers[$id];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $operations[$id] = array(
+ 'type' => 'group',
+ 'class' => array('operations-handlers-' . $id),
+ 'title' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
+ 'collapsible' => $collapsible,
+ 'children' => array(),
+ );
+
+ $operations[$id]['children']['actions'] = array(
+ 'type' => 'group',
+ 'class' => array('operations-handlers-actions-' . $id),
+ 'title' => t('Variant operations'),
+ 'children' => array(),
+ 'location' => $id,
+ );
+
+ // There needs to be a 'summary' item here for variants.
+ $operations[$id]['children']['summary'] = array(
+ 'title' => t('Summary'),
+ 'description' => t('Get a summary of the information about this variant.'),
+ 'form info' => array(
+ 'no buttons' => TRUE,
+ ),
+ 'form' => 'page_manager_handler_summary',
+ );
+
+ if ($plugin && isset($plugin['operations'])) {
+ $operations[$id]['children'] += $plugin['operations'];
+ }
+
+ $actions = &$operations[$id]['children']['actions']['children'];
+
+ $actions['clone'] = array(
+ 'title' => t('Clone'),
+ 'description' => t('Make an exact copy of this variant.'),
+ 'form' => 'page_manager_handler_clone',
+ );
+ $actions['export'] = array(
+ 'title' => t('Export'),
+ 'description' => t('Export this variant into code to import into another page.'),
+ 'form' => 'page_manager_handler_export',
+ );
+ if ($handler->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
+ $actions['delete'] = array(
+ 'title' => t('Revert'),
+ 'description' => t('Remove all changes to this variant and revert to the version in code.'),
+ 'form' => 'page_manager_handler_delete',
+ 'no update and save' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Revert'),
+ ),
+ );
+ }
+ else if ($handler->export_type != EXPORT_IN_CODE) {
+ $actions['delete'] = array(
+ 'title' => t('Delete'),
+ 'description' => t('Remove this variant from the page completely.'),
+ 'form' => 'page_manager_handler_delete',
+ 'form info' => array(
+ 'finish text' => t('Delete'),
+ 'save text' => t('Delete and save'),
+ ),
+ );
+ }
+ if (!empty($handler->disabled)) {
+ $actions['enable'] = array(
+ 'title' => t('Enable'),
+ 'description' => t('Activate this variant so that it will be in use in your system.'),
+ 'form' => 'page_manager_handler_enable',
+ 'silent' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Enable'),
+ 'save text' => t('Enable and save'),
+ ),
+ );
+ }
+ else {
+ $actions['disable'] = array(
+ 'title' => t('Disable'),
+ 'description' => t('De-activate this variant. The data will remain but the variant will not be in use on your system.'),
+ 'form' => 'page_manager_handler_disable',
+ 'silent' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Disable'),
+ 'save text' => t('Disable and save'),
+ ),
+ );
+ }
+
+ drupal_alter('page_manager_variant_operations', $operations[$id], $handler);
+ }
+ if (empty($operations)) {
+ $operations['empty'] = array(
+ 'type' => 'text',
+ 'title' => t('No variants'),
+ );
+ }
+
+ $group['children'] = $operations;
+ return $group;
+}
+
+/**
+ * Get an operation from a trail.
+ *
+ * @return array($operation, $active, $args)
+ */
+function page_manager_get_operation($operations, $trail) {
+ $args = $trail;
+ $stop = FALSE;
+ $active = array();
+ $titles = array();
+ // Drill down into operations array:
+ while (!$stop) {
+ $check = reset($args);
+ $stop = TRUE;
+ if (is_array($operations)) {
+ if (isset($operations[$check])) {
+ $active[] = $check;
+ $operation = array_shift($args);
+ // check to see if this operation has children. If so, we continue.
+ if (!isset($operations[$check]['children'])) {
+ $operations = $operations[$check];
+ }
+ else {
+ $titles[] = $operations[$check]['title'];
+ $operations = $operations[$check]['children'];
+ // continue only if the operation hs children.
+ $stop = FALSE;
+ }
+ }
+ }
+ }
+
+ return array($operations, $active, $args, $titles);
+}
+
+/**
+ * Fetch the content for an operation.
+ *
+ * First, this drills down through the arguments to find the operation, and
+ * turns whatever it finds into the active trail which is then used to
+ * hilite where we are when rendering the operation list.
+ *
+ * The arguments are discovered from the URL, and are an exact match for where
+ * the operation is in the hierarchy. For example, handlers/foo/settings will
+ * be the operation to edit the settings for the handler named foo. This comes
+ * in as an array ('handlers', 'foo', 'settings') and is used to find where the
+ * data for that operation is in the array.
+ */
+function page_manager_get_operation_content($js, &$page, $trail, $operations) {
+ list($operation, $active, $args, $titles) = page_manager_get_operation($operations, $trail);
+ // Once we've found the operation, send it off to render.
+ if ($operation) {
+ $content = _page_manager_get_operation_content($js, $page, $active, $operation, $titles, $args);
+ }
+
+ if (empty($content)) {
+ $content = _page_manager_get_operation_content($js, $page, array('summary'), $operations['summary']);
+ }
+
+ return $content;
+}
+
+/**
+ * Fetch the content for an operation, after it's been discovered from arguments.
+ *
+ * This system runs through the CTools form wizard. Each operation specifies a form
+ * or set of forms that it may use. Operations may also specify wrappers and can
+ * set their own next/finish handlers so that they can make additional things happen
+ * at the end.
+ */
+function _page_manager_get_operation_content($js, &$page, $active, $operation, $titles = array(), $args = array()) {
+ if (isset($operation['form'])) {
+ $form_info = array(
+ 'id' => 'page_manager_page',
+ 'finish text' => t('Update'),
+ 'show trail' => FALSE,
+ 'show back' => FALSE,
+ 'show return' => FALSE,
+ 'show cancel' => FALSE,
+ 'next callback' => 'page_manager_edit_page_next',
+ 'finish callback' => 'page_manager_edit_page_finish',
+ // Items specific to the 'edit' routines that will get moved over:
+ 'path' => page_manager_edit_url($page->task_name, $active) . "/%step",
+ // wrapper function to add an extra finish button.
+ 'wrapper' => 'page_manager_operation_wrapper',
+ );
+
+ // If $operation['form'] is simply a string, then it is the function
+ // name of the form.
+ if (!is_array($operation['form'])) {
+ $form_info['order'] = array(
+ 'form' => $operation['title'],
+ );
+ $form_info['forms'] = array(
+ 'form' => array('form id' => $operation['form']),
+ );
+ if (isset($operation['wrapper'])) {
+ $form_info['forms']['form']['wrapper'] = $operation['wrapper'];
+ }
+ }
+ // Otherwise it's the order and forms arrays directly.
+ else {
+ $form_info['order'] = $operation['form']['order'];
+ $form_info['forms'] = $operation['form']['forms'];
+ }
+
+ // Allow the operation to override any form info settings:
+ if (isset($operation['form info'])) {
+ foreach ($operation['form info'] as $key => $setting) {
+ $form_info[$key] = $setting;
+ }
+ }
+
+ if (!empty($page->subtask['operations include'])) {
+ // Quickly load any files necessary to display the forms.
+ $page->subtask['operations include']['function'] = 'nop';
+ ctools_plugin_get_function($page->subtask, 'operations include');
+ }
+
+ $step = array_shift($args);
+ // If step is unset, go with the basic step.
+ if (!isset($step)) {
+ $step = current(array_keys($form_info['order']));
+ }
+
+ // If it is locked, hide the buttonzzz!
+ if ($page->locked && empty($operation['even locked'])) {
+ $form_info['no buttons'] = TRUE;
+ }
+
+ ctools_include('wizard');
+ $form_state = array(
+ 'page' => $page,
+ 'type' => 'edit',
+ 'ajax' => $js && (!isset($operation['ajax']) || !empty($operation['ajax'])),
+ 'rerender' => TRUE,
+ 'trail' => $active,
+ 'task_name' => $page->task_name,
+ 'task_id' => $page->task_id,
+ 'task' => $page->task,
+ 'subtask_id' => $page->subtask_id,
+ 'subtask' => $page->subtask,
+ 'operation' => $operation,
+ );
+
+ if ($active && $active[0] == 'handlers' && isset($form_state['page']->handlers[$form_state['trail'][1]])) {
+ $form_state['handler_id'] = $form_state['trail'][1];
+ $form_state['handler'] = &$form_state['page']->handlers[$form_state['handler_id']];
+ }
+
+ if ($active && $active[0] == 'actions' && $active[1] == 'configure' && isset($form_state['page']->new_handler)) {
+ $form_state['handler_id'] = $form_state['page']->new_handler->name;
+ $form_state['handler'] = &$form_state['page']->new_handler;
+ }
+
+ $built_form = ctools_wizard_multistep_form($form_info, $step, $form_state);
+ $output = drupal_render($built_form);
+ $title = empty($form_state['title']) ? $operation['title'] : $form_state['title'];
+ $titles[] = $title;
+ $title = implode(' &raquo; ', array_filter($titles));
+
+ // If there are messages for the form, render them.
+ if ($messages = theme('status_messages')) {
+ $output = $messages . $output;
+ }
+
+ $description = isset($operation['admin description']) ? $operation['admin description'] : (isset($operation['description']) ? $operation['description'] : '');
+ $return = array(
+ 'title' => $title,
+ 'content' => $output,
+ 'description' => $description,
+ );
+
+ // Append any extra content, used for the preview which is submitted then
+ // rendered.
+ if (isset($form_state['content'])) {
+ $return['content'] .= $form_state['content'];
+ }
+
+ // If the form wanted us to go somewhere else next, pass that along.
+ if (isset($form_state['new trail'])) {
+ $return['new trail'] = $form_state['new trail'];
+ }
+ }
+ else {
+ $return = array(
+ 'title' => t('Error'),
+ 'content' => t('This operation trail does not exist.'),
+ );
+ }
+
+ $return['active'] = $active;
+ return $return;
+}
+
+function page_manager_operation_wrapper($form, &$form_state) {
+ if (empty($form_state['operation']['no update and save']) && !empty($form['buttons']['return']['#wizard type']) && $form['buttons']['return']['#wizard type']) {
+ $form['buttons']['save'] = array(
+ '#type' => 'submit',
+ '#value' => !empty($form_state['form_info']['save text']) ? $form_state['form_info']['save text'] : t('Update and save'),
+ '#wizard type' => 'finish',
+ '#attributes' => $form['buttons']['return']['#attributes'],
+ '#save' => TRUE,
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Callback generated when the an operation edit finished.
+ */
+function page_manager_edit_page_finish(&$form_state) {
+ if (empty($form_state['operation']['silent'])) {
+ if (empty($form_state['clicked_button']['#save'])) {
+ drupal_set_message(t('The page has been updated. Changes will not be permanent until you save.'));
+ }
+ else {
+ drupal_set_message(t('The page has been updated and saved.'));
+ }
+ $path = array();
+ foreach ($form_state['trail'] as $operation) {
+ $path[] = $operation;
+ $form_state['page']->changes[implode('/', $path)] = TRUE;
+ }
+ }
+
+ // If a handler was modified, set it to changed so we know to overwrite it.
+ if (isset($form_state['handler_id'])) {
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
+ }
+
+ // While we make buttons go away on locked pages, it is still possible to
+ // have a lock a appear while you were editing, and have your changes
+ // disappear. This at least warns the user that this has happened.
+ if (!empty($page->locked)) {
+ drupal_set_message(t('Unable to update changes due to lock.'));
+ }
+
+ // If the 'Update and Save' button was selected, write our cache out.
+ if (!empty($form_state['clicked_button']['#save'])) {
+ page_manager_save_page_cache($form_state['page']);
+ page_manager_clear_page_cache($form_state['page']->task_name);
+ $form_state['page'] = page_manager_get_page_cache($form_state['page']->task_name);
+ }
+ else {
+ if (empty($form_state['do not cache'])) {
+ page_manager_set_page_cache($form_state['page']);
+ }
+ }
+
+ // We basically always want to force a rerender when the forms
+ // are finished, so make sure there is a new trail.
+ if (empty($form_state['new trail'])) {
+ // force a rerender to get rid of old form items that may have changed
+ // during save.
+ $form_state['new trail'] = $form_state['trail'];
+ }
+
+ if (isset($form_state['new trail']) && empty($form_state['ajax'])) {
+ $form_state['redirect'] = page_manager_edit_url($form_state['page']->task_name, $form_state['new trail']);
+ }
+
+ $form_state['complete'] = TRUE;
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_edit_page_next(&$form_state) {
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * All we do here is clear the cache.
+ */
+function page_manager_edit_page_cancel(&$form_state) {
+ $page = $form_state['page'];
+}
+
+/**
+ * Render an operations array.
+ *
+ * This renders an array of operations into a series of nested UL statements,
+ * with ajax automatically on unless specified otherwise. Operations will
+ * automatically have the URLs generated nested.
+ *
+ * Each operation should have a 'type', which tells the renderer how to deal
+ * with it:
+ * - 'operation': An AJAX link to render. This is the default and is
+ * assumed if a type is not specified. Other fields for the operation:
+ * - - 'title': The text to display. Can be an image. Must be pre-sanitized.
+ * - - 'description': Text to place in the hover box over the link using the
+ * title attribute.
+ * - - 'arguments': Anything optional to put at the end of the URL.
+ * - - 'path': If set, overrides the default path.
+ * - - 'no operations': If set, the path will not have operations appended.
+ * - - 'no task': If set, the path will not have the task id.
+ * - - 'no link': If set, this item will just be text, not a link.
+ * - - 'ajax': If set to TRUE, ajax will be used. The default is TRUE.
+ * - - 'class': An optional class to specify for the link.
+ * - - 'form': The form to display for this operation, if using a single form.
+ * - - 'forms': An array of forms that must be paired with 'order' of this
+ * operation uses multiple forms. See wizard tool for details.
+ * - - 'order': The form order to use for multiple forms. See wizard tool for
+ * details.
+ * - - 'form info': Form info overrides for the wizard. See the wizard tool
+ * for available settings
+ * - 'group':
+ * - - 'title': The title of the link. May be HTML.
+ * - - 'title class': A class to apply to the title.
+ * - - 'children': An array of more operations that this group represents.
+ * All operations within this group will have this group's ID as part
+ * of the AJAX url to make it easier to find.
+ * - - 'class': A class to apply to the UL of the children.
+ * - - 'collapsible': If TRUE the collapsible tool will be used.
+ */
+function page_manager_render_operations(&$page, $operations, $active_trail, $attributes, $location, $parents = array()) {
+ drupal_add_library('system', 'drupal.ajax');
+
+ if (!isset($output[$location])) {
+ $output[$location] = '';
+ }
+
+ $keys = array_keys($operations);
+ $first = array_shift($keys);
+ $last = array_pop($keys);
+
+ // Make sure the 'first' and 'last' operations are part of THIS nav tree:
+ while ($keys && isset($operations[$first]['location']) && $operations[$first]['location'] != $location) {
+ $first = array_shift($keys);
+ }
+ while ($keys && isset($operations[$last]['location']) && $operations[$last]['location'] != $location) {
+ $last = array_pop($keys);
+ }
+
+ $active = reset($active_trail);
+ foreach ($operations as $id => $operation) {
+ $current_path = '';
+ if ($parents) {
+ $current_path .= implode('/', $parents) . '/';
+ }
+ $current_path .= $id;
+
+ if (empty($operation['type'])) {
+ $operation['type'] = 'operation';
+ }
+
+ // We only render an li for things in the same nav tree.
+ if (empty($operation['location']) || $operation['location'] == $location) {
+ if (!is_array($attributes['class'])) {
+ $attributes['class'] = array($attributes['class']);
+ }
+
+ $class = empty($attributes['class']) || !is_array($attributes['class']) ? array() : $attributes['class'];
+
+ if ($id == $first) {
+ $class[] = 'operation-first';
+ }
+ else if ($id == $last) {
+ $class[] = 'operation-last';
+ }
+
+ if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
+ $class[] = $operation['type'] == 'group' ? 'changed-group' : 'changed';
+ }
+ else {
+ $class[] = 'not-changed';
+ }
+
+ if ($active == $id) {
+ $class[] = $operation['type'] == 'group' ? 'active-group' : 'active';
+ }
+ else {
+ $class[] = 'not-active';
+ }
+
+ $output[$location] .= '<li class="' . implode(' ', $class) . '">';
+ }
+
+ switch ($operation['type']) {
+ case 'text':
+ $output[$location] .= $operation['title'];
+ break;
+ case 'operation':
+ $path = isset($operation['path']) ? $operation['path'] : 'admin/structure/pages/nojs/operation';
+ if (!isset($operation['no task'])) {
+ $path .= '/' . $page->task_name;
+ }
+
+ if (!isset($operation['no operations'])) {
+ $path .= '/' . $current_path;
+ if (isset($operation['arguments'])) {
+ $path .= '/' . $arguments;
+ }
+ }
+
+ $class = array('page-manager-operation');
+ if (!isset($operation['ajax']) || !empty($operation['ajax'])) {
+ $class[] = 'use-ajax';
+ }
+ if (!empty($operation['class'])) {
+ $class[] = $operation['class'];
+ }
+
+ $description = isset($operation['description']) ? $operation['description'] : '';
+ if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
+ $description .= ' ' . t('This setting contains unsaved changes.');
+ }
+
+ $output[$location] .= l($operation['title'], $path, array('attributes' => array('id' => 'page-manager-operation-' . $id, 'class' => $class, 'title' => $description), 'html' => TRUE));
+ break;
+ case 'group':
+ if ($active == $id) {
+ $trail = $active_trail;
+ array_shift($trail);
+ }
+ else {
+ $trail = array();
+ }
+ $group_location = isset($operation['location']) ? $operation['location'] : $location;
+ $temp = page_manager_render_operations($page, $operation['children'], $trail, $operation, $group_location, array_merge($parents, array($id)));
+ if ($temp) {
+ foreach ($temp as $id => $text) {
+ if (empty($output[$id])) {
+ $output[$id] = '';
+ }
+ $output[$id] .= $text;
+ }
+ }
+ break;
+ }
+
+ if (empty($operation['location']) || $operation['location'] == $location) {
+ $output[$location] .= '</li>';
+ }
+ }
+
+ if ($output[$location]) {
+ $classes = isset($attributes['class']) && is_array($attributes['class']) ? $attributes['class'] : array();
+ $classes[] = 'page-manager-operations';
+
+ $output[$location] = '<ul class="' . implode(' ', $classes) . '">' . $output[$location] . '</ul>';
+
+ if (!empty($attributes['title'])) {
+ $class = '';
+ if (isset($attributes['title class'])) {
+ $class = $attributes['title class'];
+ }
+ $title = '<div class="page-manager-group-title' . $class . '">' . $attributes['title'] . '</div>';
+
+ if (!empty($attributes['collapsible'])) {
+ $output[$location] = theme('ctools_collapsible', array('handle' => $title, 'content' => $output[$location], 'collapsed' => empty($active_trail)));
+ }
+ else {
+ $output[$location] = $title . $output[$location];
+ }
+ }
+ return $output;
+ }
+}
+
+/**
+ * Provide a simple form for saving the page manager info out of the cache.
+ */
+function page_manager_save_page_form($form, &$form_state) {
+ if (!empty($form_state['page']->changed)) {
+ $form['markup'] = array(
+ '#markup' => '<div class="changed-notification">' . t('You have unsaved changes to this page. You must select Save to write them to the database, or Cancel to discard these changes. Please note that if you have changed any form, you must submit that form before saving.') . '</div>',
+ );
+
+ // Always make sure we submit back to the proper page.
+ $form['#action'] = url('admin/structure/pages/edit/' . $form_state['page']->task_name);
+ $form['save'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#submit' => array('page_manager_save_page_form_submit'),
+ );
+
+ $form['cancel'] = array(
+ '#type' => 'submit',
+ '#value' => t('Cancel'),
+ '#submit' => array('page_manager_save_page_form_cancel'),
+ );
+ return $form;
+ }
+}
+
+/**
+ * Save the page.
+ */
+function page_manager_save_page_form_submit(&$form, &$form_state) {
+ page_manager_save_page_cache($form_state['page']);
+}
+
+/**
+ * Discard changes to the page.
+ */
+function page_manager_save_page_form_cancel($form, &$form_state) {
+ drupal_set_message(t('All pending changes have been discarded, and the page is now unlocked.'));
+ page_manager_clear_page_cache($form_state['page']->task_name);
+
+ if (!empty($form_state['page']->new)) {
+ $form_state['redirect'] = 'admin/structure/pages';
+ }
+}
+
+// --------------------------------------------------------------------------
+// Handler (variant) related forms.
+
+/**
+ * Add a new task handler.
+ */
+function page_manager_handler_add($form, &$form_state) {
+ // Get a list of possible task handlers for this task.
+ return page_manager_handler_add_form($form, $form_state);
+}
+
+/**
+ * Handler related forms.
+ */
+function page_manager_handler_add_submit(&$form, &$form_state) {
+ $cache = $form_state['page'];
+ $plugin = page_manager_get_task_handler($form_state['values']['handler']);
+
+ // Create a new handler.
+ $handler = page_manager_new_task_handler($plugin);
+ if (!empty($form_state['values']['title'])) {
+ $handler->conf['title'] = $form_state['values']['title'];
+ }
+ else {
+ $handler->conf['title'] = $plugin['title'];
+ }
+ $handler->conf['name'] = $form_state['values']['name'];
+ $cache->new_handler = $handler;
+
+ // Figure out which forms to present them with
+ $cache->forms = array();
+
+ $features = $form_state['values']['features'];
+ if (isset($features[$form_state['values']['handler']])) {
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features[$form_state['values']['handler']])));
+ }
+
+ if (isset($plugin['required forms'])) {
+ $cache->forms = array_merge($cache->forms, array_keys($plugin['required forms']));
+ }
+
+ $form_state['no_rerender'] = TRUE;
+ if (!empty($cache->forms)) {
+ // Tell the form to go to the config page.
+ drupal_set_message(t('Before this variant can be added, it must be configured. When you are finished, click "Create variant" at the end of this wizard to add this to your page.'));
+ $form_state['new trail'] = array('actions', 'configure');
+ }
+ else {
+ // It has no forms at all. Add the variant and go to its first operation.
+ page_manager_handler_add_finish($form_state);
+ }
+}
+
+/**
+ * Finish the add process and make the new handler official.
+ */
+function page_manager_handler_add_finish(&$form_state) {
+ $page = &$form_state['page'];
+ $handler = $page->new_handler;
+ page_manager_handler_add_to_page($page, $handler);
+
+ // Remove the temporary page.
+ unset($page->new_handler);
+ unset($page->forms);
+
+ // Set the new destination
+ $plugin = page_manager_get_task_handler($handler->handler);
+ if (!empty($plugin['add finish'])) {
+ $location = $plugin['add finish'];
+ }
+ else {
+ $keys = array_keys($plugin['operations']);
+ $location = reset($keys);
+ }
+
+ $form_state['new trail'] = array('handlers', $handler->name, $location);
+
+ // Pass through.
+ page_manager_edit_page_finish($form_state);
+}
+
+/**
+ * Throw away a new handler and return to the add form
+ */
+function page_manager_handler_add_cancel(&$form_state) {
+ $form_state['new trail'] = array('handlers', 'add');
+
+ // Remove the temporary page.
+ unset($page->new_handler);
+ unset($page->forms);
+}
+
+/**
+ * Provide a consistent UI for adding handlers.
+ */
+function page_manager_handler_add_form($form, $form_state, $features = array()) {
+ $task = $form_state['task'];
+ $task_handler_plugins = page_manager_get_task_handler_plugins($task);
+ if (empty($task_handler_plugins)) {
+ drupal_set_message(t('There are currently no variants available and a page may not be added. Perhaps you need to install the Panels module to get a variant?'), 'error');
+ $form['buttons']['return']['#disabled'] = TRUE;
+ return;
+ }
+
+ foreach ($task_handler_plugins as $id => $plugin) {
+ $options[$id] = $plugin['title'];
+ if (isset($plugin['add features'])) {
+ $features[$id] = $plugin['add features'];
+ }
+ }
+
+ if (!isset($form_state['type']) || $form_state['type'] != 'add') {
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Title'),
+ '#description' => t('Administrative title of this variant. If you leave blank it will be automatically assigned.'),
+ );
+
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#title' => t('Machine name'),
+ '#required' => FALSE,
+ '#description' => t("A unique machine-readable name for this variant. It must only contain lowercase letters, numbers, and underscores. This name will be used when exporting the variant. If left empty the variant's name will be used instead."),
+ '#size' => 32,
+ '#maxlength' => 32,
+ '#machine_name' => array(
+ 'exists' => 'page_manager_handler_check_machine_name',
+ 'source' => array('title'),
+ ),
+ '#field_prefix' => '<span dir="ltr">' . $form_state['task_name'] . '__',
+ '#field_suffix' => '</span>&lrm;',
+ );
+ }
+
+ $form['handler'] = array(
+ '#title' => t('Variant type'),
+ '#type' => 'select',
+ '#options' => $options,
+ );
+
+ // This set of checkboxes is not dangerous at all.
+ $form['features'] = array(
+ '#type' => 'item',
+ '#title' => t('Optional features'),
+ '#description' => t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new page is created. If you are not sure, leave these unchecked.'),
+ '#tree' => TRUE,
+ );
+
+ ctools_include('dependent');
+ foreach ($features as $plugin => $feature_list) {
+ foreach ($feature_list as $feature_id => $feature) {
+ $form['features'][$plugin][$feature_id] = array(
+ '#type' => 'checkbox',
+ '#title' => $feature,
+ );
+ if (!empty($form_state['page']->forms) && in_array($feature_id, $form_state['page']->forms)) {
+ $form['features'][$plugin][$feature_id]['#default_value'] = TRUE;
+ }
+
+ if ($plugin != 'default') {
+ $form['features'][$plugin][$feature_id] += array(
+ '#dependency' => array('edit-handler' => array($plugin)),
+ );
+ }
+ }
+ }
+
+ return $form;
+}
+
+/*
+ * Check if handler's machine-name is unique
+ */
+function page_manager_handler_check_machine_name($name, $element, $form_state) {
+ $name = $form_state['task_name'] . '__' . $name;
+
+ return count(ctools_export_load_object('page_manager_handlers', 'names', array($name)));
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_import($form, &$form_state) {
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Variant name'),
+ '#description' => t('Enter the name of the new variant.'),
+ );
+
+ if (user_access('use ctools import')) {
+ $form['object'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Paste variant code here'),
+ '#rows' => 15,
+ );
+ }
+ // Users ordinarily can't get here without the 'import' permission, due to
+ // security implications. In case they somehow do, though, disable the form
+ // widget for extra safety.
+ else {
+ $form['shoveoff'] = array(
+ '#markup' => '<div>' . t('You do not have sufficient permissions to perform this action.') . '</div>',
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Make sure that an import actually provides a handler.
+ */
+function page_manager_handler_import_validate($form, &$form_state) {
+ if (!user_access('use ctools import')) {
+ form_error($form['shoveoff'], t('You account permissions do not permit you to import.'));
+ return;
+ }
+ ob_start();
+ eval($form_state['values']['object']);
+ ob_end_clean();
+
+ if (empty($handler)) {
+ $errors = ob_get_contents();
+ if (empty($errors)) {
+ $errors = t('No variant found.');
+ }
+
+ form_error($form['object'], t('Unable to get a variant from the import. Errors reported: @errors', array('@errors' => $errors)));
+ }
+
+ $form_state['handler'] = $handler;
+}
+
+/**
+ * Clone an existing task handler into a new handler.
+ */
+function page_manager_handler_import_submit(&$form, &$form_state) {
+ $handler = $form_state['handler'];
+
+ page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
+
+ $plugin = page_manager_get_task_handler($handler->handler);
+ // It has no forms at all. Add the variant and go to its first operation.
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $handler->name, reset($keys));
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_rearrange($form, &$form_state) {
+ $page = $form_state['page'];
+
+ $form['handlers'] = array('#tree' => TRUE);
+
+ foreach ($page->handler_info as $id => $info) {
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ continue;
+ }
+ $handler = $page->handlers[$id];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $form['handlers'][$id]['title'] = array(
+ '#markup' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
+ );
+
+ $form['handlers'][$id]['weight'] = array(
+ '#type' => 'weight',
+ '#default_value' => $info['weight'],
+ '#delta' => 30,
+ );
+ }
+
+ return $form;
+}
+
+function page_manager_handler_rearrange_submit(&$form, &$form_state) {
+ $handler_info = &$form_state['page']->handler_info;
+
+ foreach ($form_state['values']['handlers'] as $id => $info) {
+ if ($handler_info[$id]['weight'] = $info['weight']) {
+ $handler_info[$id]['weight'] = $info['weight'];
+ $handler_info[$id]['changed'] |= PAGE_MANAGER_CHANGED_MOVED;
+ }
+ }
+
+ // Sort the new cache.
+ uasort($handler_info, '_page_manager_handler_sort');
+
+}
+
+/**
+ * Used as a callback to uasort to sort the task cache by weight.
+ *
+ * The 'name' field is used as a backup when weights are the same, which
+ * can happen when multiple modules put items out there at the same
+ * weight.
+ */
+function _page_manager_handler_sort($a, $b) {
+ if ($a['weight'] < $b['weight']) {
+ return -1;
+ }
+ elseif ($a['weight'] > $b['weight']) {
+ return 1;
+ }
+ elseif ($a['name'] < $b['name']) {
+ return -1;
+ }
+ elseif ($a['name'] > $b['name']) {
+ return 1;
+ }
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_delete($form, &$form_state) {
+ if ($form_state['handler']->type == t('Overridden')) {
+ $text = t('Reverting the variant will delete the variant that is in the database, reverting it to the original default variant. This deletion will not be made permanent until you click Save.');
+ }
+ else {
+ $text = t('Are you sure you want to delete this variant? This deletion will not be made permanent until you click Save.');
+ }
+ $form['markup'] = array(
+ '#markup' => '<p>' . $text . '</p>',
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler to delete a view.
+ */
+function page_manager_handler_delete_submit(&$form, &$form_state) {
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_DELETED;
+ $form_state['new trail'] = array('summary');
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_handler_export($form, &$form_state) {
+ $export = page_manager_export_task_handler($form_state['handler']);
+
+ $lines = substr_count($export, "\n");
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#default_value' => $export,
+ '#rows' => $lines,
+ );
+
+ unset($form['buttons']);
+ return $form;
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_clone($form, &$form_state) {
+ // This provides its own button because it does something totally different.
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Variant name'),
+ '#description' => t('Enter the name of the new variant.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Clone an existing task handler into a new handler.
+ */
+function page_manager_handler_clone_submit(&$form, &$form_state) {
+ $export = page_manager_export_task_handler($form_state['handler']);
+ ob_start();
+ eval($export);
+ ob_end_clean();
+
+ page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
+
+ $plugin = page_manager_get_task_handler($handler->handler);
+ // It has no forms at all. Add the variant and go to its first operation.
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $handler->name, reset($keys));
+}
+
+/**
+ * Form to enable a handler.
+ */
+function page_manager_handler_enable($form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => t('This variant is currently disabled. Enabling it will make it available in your system. This will not take effect until you save this page.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Enable the page after it has been confirmed.
+ */
+function page_manager_handler_enable_submit(&$form, &$form_state) {
+ $form_state['handler']->disabled = FALSE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = FALSE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
+ $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'actions', 'disable');
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_handler_disable($form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => t('This variant is currently enabled. Disabling it will make it unavailable in your system, and it will not be used. This will not take effect until you save this page.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_handler_summary($form, &$form_state) {
+ $handler = $form_state['handler'];
+ $page = $form_state['page'];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $form['markup'] = array(
+ '#markup' => page_manager_get_handler_summary($plugin, $handler, $page, FALSE),
+ );
+
+ return $form;
+}
+
+/**
+ * Disable the page after it has been confirmed.
+ */
+function page_manager_handler_disable_submit(&$form, &$form_state) {
+ $form_state['handler']->disabled = TRUE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = TRUE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
+ $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'actions', 'enable');
+}
+
+/**
+ * Break the lock on a page so that it can be edited.
+ */
+function page_manager_break_lock($form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => t('Breaking the lock on this page will <strong>discard</strong> any pending changes made by the locking user. Are you REALLY sure you want to do this?')
+ );
+
+ return $form;
+}
+
+/**
+ * Submit to break the lock on a page.
+ */
+function page_manager_break_lock_submit(&$form, &$form_state) {
+ $page = &$form_state['page'];
+ $form_state['page']->locked = FALSE;
+ ctools_object_cache_clear_all('page_manager_page', $page->task_name);
+ $form_state['do not cache'] = TRUE;
+ drupal_set_message(t('The lock has been cleared and all changes discarded. You may now make changes to this page.'));
+
+ $form_state['new trail'] = array('summary');
+}
+
+/**
+ * Form to enable a page.
+ */
+function page_manager_enable_form($form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => t('Enabling this page will immediately make it available in your system (there is no need to wait for a save.)'),
+ );
+
+ return $form;
+}
+
+/**
+ * Enable the page after it has been confirmed.
+ */
+function page_manager_enable_form_submit(&$form, &$form_state) {
+ $page = &$form_state['page'];
+ if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
+ $result = $function($page, FALSE);
+ menu_rebuild();
+ }
+ $form_state['new trail'] = array('actions', 'disable');
+
+ // We don't want to cause this to cache if it wasn't already. If it was
+ // cached, however, we want to update the enabled state.
+ if (empty($form_state['page']->changed)) {
+ $form_state['do not cache'] = TRUE;
+ }
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_disable_form($form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => t('Disabling this page will immediately make it unavailable in your system (there is no need to wait for a save.)'),
+ );
+
+ return $form;
+}
+
+/**
+ * Disable the page after it has been confirmed.
+ */
+function page_manager_disable_form_submit(&$form, &$form_state) {
+ $page = &$form_state['page'];
+ if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
+ $result = $function($page, TRUE);
+ menu_rebuild();
+ $form_state['new trail'] = array('actions', 'enable');
+
+ // We don't want to cause this to cache if it wasn't already. If it was
+ // cached, however, we want to update the enabled state.
+ if (empty($form_state['page']->changed)) {
+ $form_state['do not cache'] = TRUE;
+ }
+ }
+}
+
+/**
+ * Print the summary information for a page.
+ */
+function page_manager_page_summary($form, &$form_state) {
+ $page = $form_state['page'];
+
+ $output = '';
+
+/*
+ if (isset($form_state['subtask']['admin title'])) {
+ $form_state['title'] = $form_state['subtask']['admin title'];
+ }
+*/
+
+ if (isset($form_state['subtask']['admin description'])) {
+ $output .= '<div class="description">' . $form_state['subtask']['admin description'] . '</div>';
+ }
+
+ $output .= page_manager_get_page_summary($page->task, $page->subtask);
+
+ if (!empty($page->handlers)) {
+ foreach ($page->handler_info as $id => $info) {
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ continue;
+ }
+
+ $handler = $page->handlers[$id];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $output .= '<div class="handler-summary">';
+ $output .= page_manager_get_handler_summary($plugin, $handler, $page);
+ $output .= '</div>';
+
+ }
+ }
+ else {
+ $output .= '<p>' . t('This page has no variants and thus no output of its own.') . '</p>';
+ }
+
+ $links = array(
+ array(
+ 'title' => ' &raquo; ' . t('Add a new variant'),
+ 'href' => page_manager_edit_url($page->task_name, array('actions', 'add')),
+ 'html' => TRUE,
+ ),
+ );
+
+ $output .= '<div class="links">' . theme('links', array('links' => $links)) . '</div>';
+ $form['markup'] = array(
+ '#markup' => $output,
+ );
+
+ return $form;
+}
+
+/**
+ * Menu callback to enable or disable a page
+ */
+function page_manager_enable_page($disable, $js, $page) {
+ if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $page->task_name)) {
+ return MENU_ACCESS_DENIED;
+ }
+ if ($page->locked) {
+ if ($disable) {
+ drupal_set_message(t('Unable to disable due to lock.'));
+ }
+ else {
+ drupal_set_message(t('Unable to enable due to lock.'));
+ }
+ }
+ else {
+ if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
+ $result = $function($page, $disable);
+ menu_rebuild();
+
+ // We want to re-cache this if it's changed so that status is properly
+ // updated on the changed form.
+ if (!empty($page->changed)) {
+ page_manager_set_page_cache($page);
+ }
+ }
+ }
+
+ // For now $js is not actually in use on this.
+ drupal_goto('admin/structure/pages');
+}
diff --git a/sites/all/modules/ctools/page_manager/page_manager.api.php b/sites/all/modules/ctools/page_manager/page_manager.api.php
new file mode 100644
index 000000000..03e2e75f6
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/page_manager.api.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Describe hooks provided by the Page Manager module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * @todo.
+ *
+ * @param array $result
+ * @todo.
+ * @param object $page
+ * @todo.
+ */
+function hook_page_manager_operations_alter(&$result, &$page) {
+ // @todo.
+}
+
+/**
+ * @todo.
+ *
+ * @param array $operations
+ * @todo.
+ * @param object $handler
+ * @todo.
+ */
+function hook_page_manager_variant_operations_alter(&$operations, &$handler) {
+ // @todo.
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/sites/all/modules/ctools/page_manager/page_manager.info b/sites/all/modules/ctools/page_manager/page_manager.info
new file mode 100644
index 000000000..c7f4df3af
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/page_manager.info
@@ -0,0 +1,13 @@
+name = Page manager
+description = Provides a UI and API to manage pages within the site.
+core = 7.x
+dependencies[] = ctools
+package = Chaos tool suite
+version = CTOOLS_MODULE_VERSION
+
+; Information added by Drupal.org packaging script on 2015-08-19
+version = "7.x-1.9"
+core = "7.x"
+project = "ctools"
+datestamp = "1440020680"
+
diff --git a/sites/all/modules/ctools/page_manager/page_manager.install b/sites/all/modules/ctools/page_manager/page_manager.install
new file mode 100644
index 000000000..b170ce721
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/page_manager.install
@@ -0,0 +1,204 @@
+<?php
+
+/**
+ * @file
+ * Installation routines for page manager module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function page_manager_schema() {
+ // This should always point to our 'current' schema. This makes it relatively easy
+ // to keep a record of schema as we make changes to it.
+ return page_manager_schema_1();
+}
+
+/**
+ * Schema version 1 for Panels in D6.
+ */
+function page_manager_schema_1() {
+ $schema['page_manager_handlers'] = array(
+ 'export' => array(
+ 'identifier' => 'handler',
+ 'bulk export' => TRUE,
+ 'export callback' => 'page_manager_export_task_handler',
+ 'load callback' => 'page_manager_export_task_handler_load',
+ 'delete callback' => 'page_manager_delete_task_handler',
+ 'primary key' => 'did',
+ 'api' => array(
+ 'owner' => 'page_manager',
+ 'api' => 'pages_default',
+ 'minimum_version' => 1,
+ 'current_version' => 1,
+ ),
+ ),
+ 'fields' => array(
+ 'did' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
+ 'no export' => TRUE,
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this task handler. Used to identify it programmatically.',
+ ),
+ 'task' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the task this handler is for.',
+ ),
+ 'subtask' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the subtask this handler is for.',
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'handler' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the task handler being used.',
+ ),
+ 'weight' => array(
+ 'type' => 'int',
+ 'description' => 'The order in which this handler appears. Lower numbers go first.',
+ ),
+ 'conf' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of the handler, if needed.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ ),
+ 'primary key' => array('did'),
+ 'unique keys' => array(
+ 'name' => array('name'),
+ ),
+ 'indexes' => array('fulltask' => array('task', 'subtask', 'weight')),
+ );
+
+ $schema['page_manager_weights'] = array(
+ 'description' => 'Contains override weights for page_manager handlers that are in code.',
+ 'fields' => array(
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this task handler. Used to identify it programmatically.',
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'weight' => array(
+ 'type' => 'int',
+ 'description' => 'The order in which this handler appears. Lower numbers go first.',
+ ),
+ ),
+ 'primary key' => array('name'),
+ 'indexes' => array(
+ 'weights' => array('name', 'weight'),
+ ),
+ );
+
+ $schema['page_manager_pages'] = array(
+ 'description' => 'Contains page subtasks for implementing pages with arbitrary tasks.',
+ 'export' => array(
+ 'identifier' => 'page',
+ 'bulk export' => TRUE,
+ 'export callback' => 'page_manager_page_export',
+ 'api' => array(
+ 'owner' => 'page_manager',
+ 'api' => 'pages_default',
+ 'minimum_version' => 1,
+ 'current_version' => 1,
+ ),
+ ),
+ 'fields' => array(
+ 'pid' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
+ 'no export' => TRUE,
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this subtask. Used to identify it programmatically.',
+ ),
+ 'task' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'What type of page this is, so that we can use the same mechanism for creating tighter UIs for targeted pages.',
+ 'default' => 'page',
+ ),
+ 'admin_title' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Human readable title for this page subtask.',
+ ),
+ 'admin_description' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Administrative description of this item.',
+ 'object default' => '',
+ ),
+ 'path' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'The menu path that will invoke this task.',
+ ),
+ 'access' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Access configuration for this path.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'menu' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of Drupal menu visibility settings for this item.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'arguments' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Configuration of arguments for this menu item.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'conf' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of the page, if needed.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ ),
+ 'primary key' => array('pid'),
+ 'unique keys' => array(
+ 'name' => array('name'),
+ ),
+ 'indexes' => array('task' => array('task')),
+ );
+
+ return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function page_manager_install() {
+ db_update('system')
+ ->fields(array('weight' => 99))
+ ->condition('name', 'page_manager')
+ ->execute();
+}
diff --git a/sites/all/modules/ctools/page_manager/page_manager.module b/sites/all/modules/ctools/page_manager/page_manager.module
new file mode 100644
index 000000000..f3cb743e3
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/page_manager.module
@@ -0,0 +1,1349 @@
+<?php
+
+/**
+ * @file
+ * The page manager module provides a UI and API to manage pages.
+ *
+ * It defines pages, both for system pages, overrides of system pages, and
+ * custom pages using Drupal's normal menu system. It allows complex
+ * manipulations of these pages, their content, and their hierarchy within
+ * the site. These pages can be exported to code for superior revision
+ * control.
+ */
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item was moved.
+ */
+define('PAGE_MANAGER_CHANGED_MOVED', 0x01);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item edited or added.
+ */
+define('PAGE_MANAGER_CHANGED_CACHED', 0x02);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item deleted.
+ */
+define('PAGE_MANAGER_CHANGED_DELETED', 0x04);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item has had its disabled status changed.
+ */
+define('PAGE_MANAGER_CHANGED_STATUS', 0x08);
+
+// --------------------------------------------------------------------------
+// Drupal hooks
+
+/**
+ * Implements hook_permission().
+ */
+function page_manager_permission() {
+ return array(
+ 'use page manager' => array(
+ 'title' => t('Use Page Manager'),
+ 'description' => t("Allows users to use most of Page Manager's features, though restricts some of the most powerful, potentially site-damaging features. Note that even the reduced featureset still allows for enormous control over your website."),
+ 'restrict access' => TRUE,
+ ),
+ 'administer page manager' => array(
+ 'title' => t('Administer Page Manager'),
+ 'description' => t('Allows complete control over Page Manager, i.e., complete control over your site. Grant with extreme caution.'),
+ 'restrict access' => TRUE,
+ ),
+ );
+
+}
+
+/**
+ * Implements hook_ctools_plugin_directory() to let the system know
+ * where our task and task_handler plugins are.
+ */
+function page_manager_ctools_plugin_directory($owner, $plugin_type) {
+ if ($owner == 'page_manager') {
+ return 'plugins/' . $plugin_type;
+ }
+ if ($owner == 'ctools' && $plugin_type == 'cache') {
+ return 'plugins/' . $plugin_type;
+ }
+}
+
+/**
+ * Implements hook_ctools_plugin_type() to inform the plugin system that Page
+ * Manager owns task, task_handler, and page_wizard plugin types.
+ *
+ * All of these are empty because the defaults all work.
+ */
+function page_manager_ctools_plugin_type() {
+ return array(
+ 'tasks' => array(),
+ 'task_handlers' => array(),
+ 'page_wizards' => array(),
+ );
+}
+
+/**
+ * Delegated implementation of hook_menu().
+ */
+function page_manager_menu() {
+ // For some reason, some things can activate modules without satisfying
+ // dependencies. I don't know how, but this helps prevent things from
+ // whitescreening when this happens.
+ if (!module_exists('ctools')) {
+ return;
+ }
+
+ $items = array();
+ $base = array(
+ 'access arguments' => array('use page manager'),
+ 'file' => 'page_manager.admin.inc',
+ 'theme callback' => 'ajax_base_page_theme',
+ );
+
+ $items['admin/structure/pages'] = array(
+ 'title' => 'Pages',
+ 'description' => 'Add, edit and remove overridden system pages and user defined pages from the system.',
+ 'page callback' => 'page_manager_list_page',
+ ) + $base;
+
+ $items['admin/structure/pages/list'] = array(
+ 'title' => 'List',
+ 'page callback' => 'page_manager_list_page',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ ) + $base;
+
+ $items['admin/structure/pages/edit/%page_manager_cache'] = array(
+ 'title' => 'Edit',
+ 'page callback' => 'page_manager_edit_page',
+ 'page arguments' => array(4),
+ 'type' => MENU_NORMAL_ITEM,
+ ) + $base;
+
+ $items['admin/structure/pages/%ctools_js/operation/%page_manager_cache'] = array(
+ 'page callback' => 'page_manager_edit_page_operation',
+ 'page arguments' => array(3, 5),
+ 'type' => MENU_NORMAL_ITEM,
+ ) + $base;
+
+ $items['admin/structure/pages/%ctools_js/enable/%page_manager_cache'] = array(
+ 'page callback' => 'page_manager_enable_page',
+ 'page arguments' => array(FALSE, 3, 5),
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ $items['admin/structure/pages/%ctools_js/disable/%page_manager_cache'] = array(
+ 'page callback' => 'page_manager_enable_page',
+ 'page arguments' => array(TRUE, 3, 5),
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ $tasks = page_manager_get_tasks();
+
+ // Provide menu items for each task.
+ foreach ($tasks as $task_id => $task) {
+ // Allow the task to add its own menu items.
+ if ($function = ctools_plugin_get_function($task, 'hook menu')) {
+ $function($items, $task);
+ }
+
+ // And for those that provide subtasks, provide menu items for them, as well.
+ foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
+ // Allow the task to add its own menu items.
+ if ($function = ctools_plugin_get_function($task, 'hook menu')) {
+ $function($items, $subtask);
+ }
+ }
+ }
+
+ return $items;
+}
+
+function page_manager_admin_paths() {
+ /* @todo FIX ME this is a major resource suck. */
+ return;
+
+ $items = array();
+ ctools_include('page', 'page_manager', 'plugins/tasks');
+ $pages = page_manager_page_load_all();
+ foreach ($pages as $page) {
+ // Make sure the page we're on is set to be an administrative path and that
+ // it is not set to be a frontpage path.
+ if ((isset($page->conf['admin_paths']) && $page->conf['admin_paths']) && (!isset($page->make_frontpage) || !$page->make_frontpage)) {
+ $path_parts = explode('/', $page->path);
+ foreach ($path_parts as $key => $part) {
+ if (strpos($part, '%') !== FALSE || strpos($part, '!') !== FALSE) {
+ $path_parts[$key] = '*';
+ }
+ }
+ $path = implode('/', $path_parts);
+ if ($page->menu['type'] == 'default tab') {
+ array_pop($path_parts);
+ $parent_path = implode('/', $path_parts);
+ $items[$parent_path] = TRUE;
+ }
+ $items[$path] = TRUE;
+ }
+ }
+ return $items;
+}
+
+/**
+ * Implements hook_menu_alter.
+ *
+ * Get a list of all tasks and delegate to them.
+ */
+function page_manager_menu_alter(&$items) {
+ // For some reason, some things can activate modules without satisfying
+ // dependencies. I don't know how, but this helps prevent things from
+ // whitescreening when this happens.
+ if (!module_exists('ctools')) {
+ return;
+ }
+
+ $tasks = page_manager_get_tasks();
+
+ foreach ($tasks as $task) {
+ if ($function = ctools_plugin_get_function($task, 'hook menu alter')) {
+ $function($items, $task);
+ }
+ // let the subtasks alter the menu items too.
+ foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
+ if ($function = ctools_plugin_get_function($subtask, 'hook menu alter')) {
+ $function($items, $subtask);
+ }
+ }
+ }
+
+ // Override the core node revisions display to use the configured Page
+ // display handler.
+ if (!variable_get('page_manager_node_view_disabled', TRUE) && isset($items['node/%node/revisions/%/view'])) {
+ // Abstract the basic settings.
+ $item = array(
+ // Handle the page arguments.
+ 'load arguments' => array(3),
+ 'page arguments' => array(1, TRUE),
+
+ // Replace the normal node_show call with Page Manager's node view.
+ 'page callback' => 'page_manager_node_view_page',
+
+ // Provide the correct path to the Page Manager file.
+ 'file' => 'node_view.inc',
+ 'file path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ );
+ // Re-build the menu item using the normal values from node.module.
+ $items['node/%node/revisions/%/view'] = array(
+ 'title' => 'Revisions',
+ 'access callback' => '_node_revision_access',
+ 'access arguments' => array(1),
+ ) + $item;
+ }
+
+ return $items;
+}
+
+/*
+ * Implements hook_theme()
+ */
+function page_manager_theme() {
+ // For some reason, some things can activate modules without satisfying
+ // dependencies. I don't know how, but this helps prevent things from
+ // whitescreening when this happens.
+ if (!module_exists('ctools')) {
+ return;
+ }
+
+ $base = array(
+ 'path' => drupal_get_path('module', 'page_manager') . '/theme',
+ 'file' => 'page_manager.theme.inc',
+ );
+
+ $items = array(
+ 'page_manager_handler_rearrange' => array(
+ 'render element' => 'form',
+ ) + $base,
+ 'page_manager_edit_page' => array(
+ 'template' => 'page-manager-edit-page',
+ 'variables' => array('page' => NULL, 'save' => NULL, 'operations' => array(), 'content' => array()),
+ ) + $base,
+ 'page_manager_lock' => array(
+ 'variables' => array('page' => array()),
+ ) + $base,
+ 'page_manager_changed' => array(
+ 'variables' => array('text' => NULL, 'description' => NULL),
+ ) + $base,
+ );
+
+ // Allow task plugins to have theme registrations by passing through:
+ $tasks = page_manager_get_tasks();
+
+ // Provide menu items for each task.
+ foreach ($tasks as $task_id => $task) {
+ if ($function = ctools_plugin_get_function($task, 'hook theme')) {
+ $function($items, $task);
+ }
+ }
+
+ return $items;
+}
+
+// --------------------------------------------------------------------------
+// Page caching
+//
+// The page cache is used to store a page temporarily, using the ctools object
+// cache. When loading from the page cache, it will either load the cached
+// version, or if there is not one, load the real thing and create a cache
+// object which can then be easily stored.
+
+/**
+ * Get the cached changes to a given task handler.
+ */
+function page_manager_get_page_cache($task_name) {
+ $caches = drupal_static(__FUNCTION__, array());
+ if (!isset($caches[$task_name])) {
+ ctools_include('object-cache');
+ $cache = ctools_object_cache_get('page_manager_page', $task_name);
+ if (!$cache) {
+ $cache = new stdClass();
+ $cache->task_name = $task_name;
+ list($cache->task_id, $cache->subtask_id) = page_manager_get_task_id($cache->task_name);
+
+ $cache->task = page_manager_get_task($cache->task_id);
+ if (empty($cache->task)) {
+ return FALSE;
+ }
+
+ if ($cache->subtask_id) {
+ $cache->subtask = page_manager_get_task_subtask($cache->task, $cache->subtask_id);
+ if (empty($cache->subtask)) {
+ return FALSE;
+ }
+ }
+ else {
+ $cache->subtask = $cache->task;
+ $cache->subtask['name'] = '';
+ }
+
+ $cache->handlers = page_manager_load_sorted_handlers($cache->task, $cache->subtask_id);
+ $cache->handler_info = array();
+ foreach ($cache->handlers as $id => $handler) {
+ $cache->handler_info[$id] = array(
+ 'weight' => $handler->weight,
+ 'changed' => FALSE,
+ 'name' => $id,
+ );
+ }
+ }
+ else {
+ // ensure the task is loaded.
+ page_manager_get_task($cache->task_id);
+ }
+
+ if ($task_name != '::new') {
+ $cache->locked = ctools_object_cache_test('page_manager_page', $task_name);
+ }
+ else {
+ $cache->locked = FALSE;
+ }
+
+ $caches[$task_name] = $cache;
+ }
+
+ return $caches[$task_name];
+}
+
+/**
+ * Store changes to a task handler in the object cache.
+ */
+function page_manager_set_page_cache($page) {
+ if (!empty($page->locked)) {
+ return;
+ }
+
+ if (empty($page->task_name)) {
+ return;
+ }
+
+ ctools_include('object-cache');
+ $page->changed = TRUE;
+ $cache = ctools_object_cache_set('page_manager_page', $page->task_name, $page);
+}
+
+/**
+ * Remove an item from the object cache.
+ */
+function page_manager_clear_page_cache($name) {
+ ctools_include('object-cache');
+ ctools_object_cache_clear('page_manager_page', $name);
+}
+
+/**
+ * Write all changes from the page cache and clear it out.
+ */
+function page_manager_save_page_cache($cache) {
+ // Save the subtask:
+ if ($function = ctools_plugin_get_function($cache->task, 'save subtask callback')) {
+ $function($cache->subtask, $cache);
+ }
+
+ // Iterate through handlers and save/delete/update as necessary.
+ // Go through each of the task handlers, check to see if it needs updating,
+ // and update it if so.
+ foreach ($cache->handler_info as $id => $info) {
+ $handler = &$cache->handlers[$id];
+ // If it has been marked for deletion, delete it.
+
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ page_manager_delete_task_handler($handler);
+ }
+ // If it has been somehow edited (or added), write the cached version
+ elseif ($info['changed'] & PAGE_MANAGER_CHANGED_CACHED) {
+ // Make sure we get updated weight from the form for this.
+ $handler->weight = $info['weight'];
+ page_manager_save_task_handler($handler);
+ }
+ // Otherwise, check to see if it has moved and, if so, update the weight.
+ elseif ($info['weight'] != $handler->weight) {
+ // Theoretically we could only do this for in code objects, but since our
+ // load mechanism checks for all, this is less database work.
+ page_manager_update_task_handler_weight($handler, $info['weight']);
+ }
+
+ // Set enable/disabled status.
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_STATUS) {
+ ctools_include('export');
+ ctools_export_set_object_status($cache->handlers[$id], $info['disabled']);
+ }
+ }
+
+ page_manager_clear_page_cache($cache->task_name);
+
+ if (!empty($cache->path_changed) || !empty($cache->new)) {
+ // Force a menu rebuild to make sure the menu entries are set.
+ menu_rebuild();
+ }
+ cache_clear_all();
+}
+
+/**
+ * Menu callback to load a page manager cache object for menu callbacks.
+ */
+function page_manager_cache_load($task_name) {
+ // load context plugin as there may be contexts cached here.
+ ctools_include('context');
+ return page_manager_get_page_cache($task_name);
+}
+
+/**
+ * Generate a unique name for a task handler.
+ *
+ * Task handlers need to be named but they aren't allowed to set their own
+ * names. Instead, they are named based upon their parent task and type.
+ */
+function page_manager_handler_get_name($task_name, $handlers, $handler) {
+ $base = str_replace('-', '_', $task_name);
+ $name = '';
+
+ // Optional machine name.
+ if (!empty($handler->conf['name'])) {
+ $name = $base . '__' . $handler->conf['name'];
+ if (count(ctools_export_load_object('page_manager_handlers', 'names', array($name)))) {
+ $name = '';
+ }
+ }
+
+ // If no machine name was provided or the name is in use, generate a unique name.
+ if (empty($name)) {
+ $base .= '__' . $handler->handler;
+
+ // Use the ctools uuid generator to generate a unique id.
+ $name = $base . '_' . ctools_uuid_generate();
+ }
+
+ return $name;
+}
+
+/**
+ * Import a handler into a page.
+ *
+ * This is used by both import and clone, since clone just exports the
+ * handler and immediately imports it.
+ */
+function page_manager_handler_add_to_page(&$page, &$handler, $title = NULL) {
+ $last = end($page->handler_info);
+ $handler->weight = $last ? $last['weight'] + 1 : 0;
+ $handler->task = $page->task_id;
+ $handler->subtask = $page->subtask_id;
+ $handler->export_type = EXPORT_IN_DATABASE;
+ $handler->type = t('Normal');
+
+ if ($title) {
+ $handler->conf['title'] = $title;
+ $handler->conf['name'] = trim(preg_replace('/[^a-z0-9_]+/', '-', strtolower($title)), '-');
+ }
+ else {
+ $handler->conf['name'] = '';
+ }
+
+ $name = page_manager_handler_get_name($page->task_name, $page->handlers, $handler);
+
+ $handler->name = $name;
+
+ $page->handlers[$name] = $handler;
+ $page->handler_info[$name] = array(
+ 'weight' => $handler->weight,
+ 'name' => $handler->name,
+ 'changed' => PAGE_MANAGER_CHANGED_CACHED,
+ );
+}
+
+// --------------------------------------------------------------------------
+// Database routines
+//
+// This includes fetching plugins and plugin info as well as specialized
+// fetch methods to get groups of task handlers per task.
+
+/**
+ * Load a single task handler by name.
+ *
+ * Handlers can come from multiple sources; either the database or by normal
+ * export method, which is handled by the ctools library, but handlers can
+ * also be bundled with task/subtask. We have to check there and perform
+ * overrides as appropriate.
+ *
+ * Handlers bundled with the task are of a higher priority than default
+ * handlers provided by normal code, and are of a lower priority than
+ * the database, so we have to check the source of handlers when we have
+ * multiple to choose from.
+ */
+function page_manager_load_task_handler($task, $subtask_id, $name) {
+ ctools_include('export');
+ $result = ctools_export_load_object('page_manager_handlers', 'names', array($name));
+ $handlers = page_manager_get_default_task_handlers($task, $subtask_id);
+ return page_manager_compare_task_handlers($result, $handlers, $name);
+}
+
+/**
+ * Load all task handlers for a given task/subtask.
+ */
+function page_manager_load_task_handlers($task, $subtask_id = NULL, $default_handlers = NULL) {
+ ctools_include('export');
+ $conditions = array(
+ 'task' => $task['name'],
+ );
+
+ if (isset($subtask_id)) {
+ $conditions['subtask'] = $subtask_id;
+ }
+
+ $handlers = ctools_export_load_object('page_manager_handlers', 'conditions', $conditions);
+ $defaults = isset($default_handlers) ? $default_handlers : page_manager_get_default_task_handlers($task, $subtask_id);
+ foreach ($defaults as $name => $default) {
+ $result = page_manager_compare_task_handlers($handlers, $defaults, $name);
+
+ if ($result) {
+ $handlers[$name] = $result;
+ // Ensure task and subtask are correct, because it's easy to change task
+ // names when editing a default and fail to do it on the associated handlers.
+ $result->task = $task['name'];
+ $result->subtask = $subtask_id;
+ }
+ }
+
+ // Override weights from the weight table.
+ if ($handlers) {
+ $names = array();
+ $placeholders = array();
+ foreach ($handlers as $handler) {
+ $names[] = $handler->name;
+ $placeholders[] = "'%s'";
+ }
+
+ $result = db_query('SELECT name, weight FROM {page_manager_weights} WHERE name IN (:names)', array(':names' => $names));
+ foreach ($result as $weight) {
+ $handlers[$weight->name]->weight = $weight->weight;
+ }
+ }
+
+ return $handlers;
+}
+
+/**
+ * Get the default task handlers from a task, if they exist.
+ *
+ * Tasks can contain 'default' task handlers which are provided by the
+ * default task. Because these can come from either the task or the
+ * subtask, the logic is abstracted to reduce code duplication.
+ */
+function page_manager_get_default_task_handlers($task, $subtask_id) {
+ // Load default handlers that are provied by the task/subtask itself.
+ $handlers = array();
+ if ($subtask_id) {
+ $subtask = page_manager_get_task_subtask($task, $subtask_id);
+ if (isset($subtask['default handlers'])) {
+ $handlers = $subtask['default handlers'];
+ }
+ }
+ else if (isset($task['default handlers'])) {
+ $handlers = $task['default handlers'];
+ }
+
+ return $handlers;
+}
+
+/**
+ * Compare a single task handler from two lists and provide the correct one.
+ *
+ * Task handlers can be gotten from multiple sources. As exportable objects,
+ * they can be provided by default hooks and the database. But also, because
+ * they are tightly bound to tasks, they can also be provided by default
+ * tasks. This function reconciles where to pick up a task handler between
+ * the exportables list and the defaults provided by the task itself.
+ *
+ * @param $result
+ * A list of handlers provided by export.inc
+ * @param $handlers
+ * A list of handlers provided by the default task.
+ * @param $name
+ * Which handler to compare.
+ * @return
+ * Which handler to use, if any. May be NULL.
+ */
+function page_manager_compare_task_handlers($result, $handlers, $name) {
+ // Compare our special default handler against the actual result, if
+ // any, and do the right thing.
+ if (!isset($result[$name]) && isset($handlers[$name])) {
+ $handlers[$name]->type = t('Default');
+ $handlers[$name]->export_type = EXPORT_IN_CODE;
+ return $handlers[$name];
+ }
+ else if (isset($result[$name]) && !isset($handlers[$name])) {
+ return $result[$name];
+ }
+ else if (isset($result[$name]) && isset($handlers[$name])) {
+ if ($result[$name]->export_type & EXPORT_IN_DATABASE) {
+ $result[$name]->type = t('Overridden');
+ $result[$name]->export_type = $result[$name]->export_type | EXPORT_IN_CODE;
+ return $result[$name];
+ }
+ else {
+ // In this case, our default is a higher priority than the standard default.
+ $handlers[$name]->type = t('Default');
+ $handlers[$name]->export_type = EXPORT_IN_CODE;
+ return $handlers[$name];
+ }
+ }
+}
+
+/**
+ * Load all task handlers for a given task and subtask and sort them.
+ */
+function page_manager_load_sorted_handlers($task, $subtask_id = NULL, $enabled = FALSE) {
+ $handlers = page_manager_load_task_handlers($task, $subtask_id);
+ if ($enabled) {
+ foreach ($handlers as $id => $handler) {
+ if (!empty($handler->disabled)) {
+ unset($handlers[$id]);
+ }
+ }
+ }
+ uasort($handlers, 'page_manager_sort_task_handlers');
+ return $handlers;
+}
+
+/**
+ * Callback for uasort to sort task handlers.
+ *
+ * Task handlers are sorted by weight then by name.
+ */
+function page_manager_sort_task_handlers($a, $b) {
+ if ($a->weight < $b->weight) {
+ return -1;
+ }
+ elseif ($a->weight > $b->weight) {
+ return 1;
+ }
+ elseif ($a->name < $b->name) {
+ return -1;
+ }
+ elseif ($a->name > $b->name) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Write a task handler to the database.
+ */
+function page_manager_save_task_handler(&$handler) {
+ $update = (isset($handler->did)) ? array('did') : array();
+ // Let the task handler respond to saves:
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'save')) {
+ $function($handler, $update);
+ }
+
+ drupal_write_record('page_manager_handlers', $handler, $update);
+ db_delete('page_manager_weights')
+ ->condition('name', $handler->name)
+ ->execute();
+
+ // If this was previously a default handler, we may have to write task handlers.
+ if (!$update) {
+ // @todo wtf was I going to do here?
+ }
+ return $handler;
+}
+
+/**
+ * Remove a task handler.
+ */
+function page_manager_delete_task_handler($handler) {
+ // Let the task handler respond to saves:
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'delete')) {
+ $function($handler);
+ }
+ db_delete('page_manager_handlers')
+ ->condition('name', $handler->name)
+ ->execute();
+ db_delete('page_manager_weights')
+ ->condition('name', $handler->name)
+ ->execute();
+}
+
+/**
+ * Export a task handler into code suitable for import or use as a default
+ * task handler.
+ */
+function page_manager_export_task_handler($handler, $indent = '') {
+ ctools_include('export');
+ ctools_include('plugins');
+ $handler = clone $handler;
+
+ $append = '';
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'export')) {
+ $append = $function($handler, $indent);
+ }
+
+ $output = ctools_export_object('page_manager_handlers', $handler, $indent);
+ $output .= $append;
+
+ return $output;
+}
+
+/**
+ * Loads page manager handler for export.
+ *
+ * Callback to load page manager handler within ctools_export_crud_load().
+ *
+ * @param string $name
+ * The name of the handler to load.
+ *
+ * @return
+ * Loaded page manager handler object, extended with external properties.
+ */
+function page_manager_export_task_handler_load($name) {
+ $table = 'page_manager_handlers';
+ $schema = ctools_export_get_schema($table);
+ $export = $schema['export'];
+
+ $result = ctools_export_load_object($table, 'names', array($name));
+ if (isset($result[$name])) {
+ $handler = $result[$name];
+
+ // Weight is stored in additional table so that in-code task handlers
+ // don't need to get written to the database just because they have their
+ // weight changed. Therefore, handler could have no correspondent database
+ // entry. Revert will not be performed for this handler and the weight
+ // will not be reverted. To make possible revert of the weight field
+ // export_type must simulate that the handler is stored in the database.
+ $handler->export_type = EXPORT_IN_DATABASE;
+
+ // Also, page manager handler weight should be overriden with correspondent
+ // weight from page_manager_weights table, if there is one.
+ $result = db_query('SELECT weight FROM {page_manager_weights} WHERE name = (:names)', array(':names' => $handler->name))->fetchField();
+ if (is_numeric($result)) {
+ $handler->weight = $result;
+ }
+ return $handler;
+ }
+}
+
+/**
+ * Create a new task handler object.
+ *
+ * @param $plugin
+ * The plugin this task handler is created from.
+ */
+function page_manager_new_task_handler($plugin) {
+ // Generate a unique name. Unlike most named objects, we don't let people choose
+ // names for task handlers because they mostly don't make sense.
+
+ // Create a new, empty handler object.
+ $handler = new stdClass;
+ $handler->title = $plugin['title'];
+ $handler->task = NULL;
+ $handler->subtask = NULL;
+ $handler->name = NULL;
+ $handler->handler = $plugin['name'];
+ $handler->weight = 0;
+ $handler->conf = array();
+
+ // These are provided by the core export API provided by ctools and we
+ // set defaults here so that we don't cause notices. Perhaps ctools should
+ // provide a way to do this for us so we don't have to muck with it.
+ $handler->export_type = EXPORT_IN_DATABASE;
+ $handler->type = t('Local');
+
+ if (isset($plugin['default conf'])) {
+ if (is_array($plugin['default conf'])) {
+ $handler->conf = $plugin['default conf'];
+ }
+ else if (function_exists($plugin['default conf'])) {
+ $handler->conf = $plugin['default conf']($handler);
+ }
+ }
+
+ return $handler;
+}
+
+/**
+ * Set an overidden weight for a task handler.
+ *
+ * We do this so that in-code task handlers don't need to get written
+ * to the database just because they have their weight changed.
+ */
+function page_manager_update_task_handler_weight($handler, $weight) {
+ db_delete('page_manager_weights')
+ ->condition('name', $handler->name)
+ ->execute();
+ db_insert('page_manager_weights')
+ ->fields(array(
+ 'name' => $handler->name,
+ 'weight' => $weight,
+ ))
+ ->execute();
+}
+
+
+/**
+ * Shortcut function to get task plugins.
+ */
+function page_manager_get_tasks() {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'tasks');
+}
+
+/**
+ * Shortcut function to get a task plugin.
+ */
+function page_manager_get_task($id) {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'tasks', $id);
+}
+
+/**
+ * Get all tasks for a given type.
+ */
+function page_manager_get_tasks_by_type($type) {
+ ctools_include('plugins');
+ $all_tasks = ctools_get_plugins('page_manager', 'tasks');
+ $tasks = array();
+ foreach ($all_tasks as $id => $task) {
+ if (isset($task['task type']) && $task['task type'] == $type) {
+ $tasks[$id] = $task;
+ }
+ }
+
+ return $tasks;
+}
+
+/**
+ * Fetch all subtasks for a page managertask.
+ *
+ * @param $task
+ * A loaded $task plugin object.
+ */
+function page_manager_get_task_subtasks($task) {
+ if (empty($task['subtasks'])) {
+ return array();
+ }
+
+ if ($function = ctools_plugin_get_function($task, 'subtasks callback')) {
+ $retval = $function($task);
+ if (is_array($retval)) {
+ return $retval;
+ }
+ }
+
+ return array();
+}
+
+/**
+ * Fetch all subtasks for a page managertask.
+ *
+ * @param $task
+ * A loaded $task plugin object.
+ * @param $subtask_id
+ * The subtask ID to load.
+ */
+function page_manager_get_task_subtask($task, $subtask_id) {
+ if (empty($task['subtasks'])) {
+ return;
+ }
+
+ if ($function = ctools_plugin_get_function($task, 'subtask callback')) {
+ return $function($task, $subtask_id);
+ }
+}
+
+/**
+ * Shortcut function to get task handler plugins.
+ */
+function page_manager_get_task_handlers() {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'task_handlers');
+}
+
+/**
+ * Shortcut function to get a task handler plugin.
+ */
+function page_manager_get_task_handler($id) {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'task_handlers', $id);
+}
+
+/**
+ * Retrieve a list of all applicable task handlers for a given task.
+ *
+ * This looks at the $task['handler type'] and compares that to $task_handler['handler type'].
+ * If the task has no type, the id of the task is used instead.
+ */
+function page_manager_get_task_handler_plugins($task, $all = FALSE) {
+ $type = isset($task['handler type']) ? $task['handler type'] : $task['name'];
+ $name = $task['name'];
+
+ $handlers = array();
+ $task_handlers = page_manager_get_task_handlers();
+ foreach ($task_handlers as $id => $handler) {
+ $task_type = is_array($handler['handler type']) ? $handler['handler type'] : array($handler['handler type']);
+ if (in_array($type, $task_type) || in_array($name, $task_type)) {
+ if ($all || !empty($handler['visible'])) {
+ $handlers[$id] = $handler;
+ }
+ }
+ }
+
+ return $handlers;
+}
+
+/**
+ * Get the title for a given handler.
+ *
+ * If the plugin has no 'admin title' function, the generic title of the
+ * plugin is used instead.
+ */
+function page_manager_get_handler_title($plugin, $handler, $task, $subtask_id) {
+ $function = ctools_plugin_get_function($plugin, 'admin title');
+ if ($function) {
+ return $function($handler, $task, $subtask_id);
+ }
+ else {
+ return $plugin['title'];
+ }
+}
+
+/**
+ * Get the admin summary (additional info) for a given handler.
+ */
+function page_manager_get_handler_summary($plugin, $handler, $page, $title = TRUE) {
+ if ($function = ctools_plugin_get_function($plugin, 'admin summary')) {
+ return $function($handler, $page->task, $page->subtask, $page, $title);
+ }
+}
+
+/**
+ * Get the admin summary (additional info) for a given page.
+ */
+function page_manager_get_page_summary($task, $subtask) {
+ if ($function = ctools_plugin_get_function($subtask, 'admin summary')) {
+ return $function($task, $subtask);
+ }
+}
+
+/**
+ * Split a task name into a task id and subtask id, if applicable.
+ */
+function page_manager_get_task_id($task_name) {
+ if (strpos($task_name, '-') !== FALSE) {
+ return explode('-', $task_name, 2);
+ }
+ else {
+ return array($task_name, NULL);
+ }
+}
+
+/**
+ * Turn a task id + subtask_id into a task name.
+ */
+function page_manager_make_task_name($task_id, $subtask_id) {
+ if ($subtask_id) {
+ return $task_id . '-' . $subtask_id;
+ }
+ else {
+ return $task_id;
+ }
+}
+
+/**
+ * Get the render function for a handler.
+ */
+function page_manager_get_renderer($handler) {
+ return ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'render');
+}
+
+// --------------------------------------------------------------------------
+// Functions existing on behalf of tasks and task handlers
+
+
+/**
+ * Page manager arg load function because menu system will not load extra
+ * files for these; they must be in a .module.
+ */
+function pm_arg_load($value, $subtask, $argument) {
+ page_manager_get_task('page');
+ return _pm_arg_load($value, $subtask, $argument);
+}
+
+/**
+ * Special arg_load function to use %menu_tail like functionality to
+ * get everything after the arg together as a single value.
+ */
+function pm_arg_tail_load($value, $subtask, $argument, $map) {
+ $value = implode('/', array_slice($map, $argument));
+ page_manager_get_task('page');
+ return _pm_arg_load($value, $subtask, $argument);
+}
+
+/**
+ * Special menu _load() function for the user:uid argument.
+ *
+ * This is just the normal page manager argument. It only exists so that
+ * the to_arg can exist.
+ */
+function pm_uid_arg_load($value, $subtask, $argument) {
+ page_manager_get_task('page');
+ return _pm_arg_load($value, $subtask, $argument);
+}
+
+/**
+ * to_arg function for the user:uid argument to provide the arg for the
+ * current global user.
+ */
+function pm_uid_arg_to_arg($arg) {
+ return user_uid_optional_to_arg($arg);
+}
+
+/**
+ * Callback for access control ajax form on behalf of page.inc task.
+ *
+ * Returns the cached access config and contexts used.
+ */
+function page_manager_page_ctools_access_get($argument) {
+ $page = page_manager_get_page_cache($argument);
+
+ $contexts = array();
+
+ // Load contexts based on argument data:
+ if ($arguments = _page_manager_page_get_arguments($page->subtask['subtask'])) {
+ $contexts = ctools_context_get_placeholders_from_argument($arguments);
+ }
+
+ return array($page->subtask['subtask']->access, $contexts);
+}
+
+/**
+ * Callback for access control ajax form on behalf of page.inc task.
+ *
+ * Writes the changed access to the cache.
+ */
+function page_manager_page_ctools_access_set($argument, $access) {
+ $page = page_manager_get_page_cache($argument);
+ $page->subtask['subtask']->access = $access;
+ page_manager_set_page_cache($page);
+}
+
+/**
+ * Callback for access control ajax form on behalf of context task handler.
+ *
+ * Returns the cached access config and contexts used.
+ */
+function page_manager_task_handler_ctools_access_get($argument) {
+ list($task_name, $name) = explode('*', $argument);
+ $page = page_manager_get_page_cache($task_name);
+ if (empty($name)) {
+ $handler = &$page->new_handler;
+ }
+ else {
+ $handler = &$page->handlers[$name];
+ }
+
+ if (!isset($handler->conf['access'])) {
+ $handler->conf['access'] = array();
+ }
+
+ ctools_include('context-task-handler');
+
+ $contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
+
+ return array($handler->conf['access'], $contexts);
+}
+
+/**
+ * Callback for access control ajax form on behalf of context task handler.
+ *
+ * Writes the changed access to the cache.
+ */
+function page_manager_task_handler_ctools_access_set($argument, $access) {
+ list($task_name, $name) = explode('*', $argument);
+ $page = page_manager_get_page_cache($task_name);
+ if (empty($name)) {
+ $handler = &$page->new_handler;
+ }
+ else {
+ $handler = &$page->handlers[$name];
+ }
+
+ $handler->conf['access'] = $access;
+ page_manager_set_page_cache($page);
+}
+
+/**
+ * Form a URL to edit a given page given the trail.
+ */
+function page_manager_edit_url($task_name, $trail = array()) {
+ if (!is_array($trail)) {
+ $trail = array($trail);
+ }
+
+ if (empty($trail) || $trail == array('summary')) {
+ return "admin/structure/pages/edit/$task_name";
+ }
+
+ return 'admin/structure/pages/nojs/operation/' . $task_name . '/' . implode('/', $trail);
+}
+
+/**
+ * Watch menu links during the menu rebuild, and re-parent things if we need to.
+ */
+function page_manager_menu_link_alter(&$item) {
+ return;
+/** -- disabled, concept code --
+ static $mlids = array();
+ // Keep an array of mlids as links are saved that we can use later.
+ if (isset($item['mlid'])) {
+ $mlids[$item['path']] = $item['mlid'];
+ }
+
+ if (isset($item['parent_path'])) {
+ if (isset($mlids[$item['parent_path']])) {
+ $item['plid'] = $mlids[$item['parent_path']];
+ }
+ else {
+ // Since we didn't already see an mlid, let's check the database for one.
+ $mlid = db_query('SELECT mlid FROM {menu_links} WHERE router_path = :path', array('path' => $item['parent_path']))->fetchField();
+ if ($mlid) {
+ $item['plid'] = $mlid;
+ }
+ }
+ }
+ */
+}
+
+/**
+ * Callback to list handlers available for export.
+ */
+function page_manager_page_manager_handlers_list() {
+ $list = $types = array();
+ $tasks = page_manager_get_tasks();
+ foreach ($tasks as $type => $info) {
+ if (empty($info['non-exportable'])) {
+ $types[] = $type;
+ }
+ }
+
+ $handlers = ctools_export_load_object('page_manager_handlers');
+ foreach ($handlers as $handler) {
+ if (in_array($handler->task, $types)) {
+ $plugin = page_manager_get_task_handler($handler->handler);
+ $title = page_manager_get_handler_title($plugin, $handler, $tasks[$handler->task], $handler->subtask);
+
+ if ($title) {
+ $list[$handler->name] = check_plain("$handler->task: $title ($handler->name)");
+ }
+ else {
+ $list[$handler->name] = check_plain("$handler->task: ($handler->name)");
+ }
+ }
+ }
+ return $list;
+}
+
+/**
+ * Callback to bulk export page manager pages.
+ */
+function page_manager_page_manager_pages_to_hook_code($names = array(), $name = 'foo') {
+ $schema = ctools_export_get_schema('page_manager_pages');
+ $export = $schema['export'];
+ $objects = ctools_export_load_object('page_manager_pages', 'names', array_values($names));
+ if ($objects) {
+ $code = "/**\n";
+ $code .= " * Implements hook_{$export['default hook']}()\n";
+ $code .= " */\n";
+ $code .= "function " . $name . "_{$export['default hook']}() {\n";
+ foreach ($objects as $object) {
+ // Have to implement our own because this export func sig requires it
+ $code .= $export['export callback']($object, TRUE, ' ');
+ $code .= " \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n";
+ }
+ $code .= " return \${$export['identifier']}s;\n";
+ $code .= "}\n";
+ return $code;
+ }
+}
+
+/**
+ * Get the current page information.
+ *
+ * @return $page
+ * An array containing the following information.
+ *
+ * - 'name': The name of the page as used in the page manager admin UI.
+ * - 'task': The plugin for the task in use. If this is a system page it
+ * will contain information about that page, such as what functions
+ * it uses.
+ * - 'subtask': The plugin for the subtask. If this is a custom page, this
+ * will contain information about that custom page. See 'subtask' in this
+ * array to get the actual page object.
+ * - 'handler': The actual handler object used. If using panels, see
+ * $page['handler']->conf['display'] for the actual panels display
+ * used to render.
+ * - 'contexts': The context objects used to render this page.
+ * - 'arguments': The raw arguments from the URL used on this page.
+ */
+function page_manager_get_current_page($page = NULL) {
+ static $current = array();
+ if (isset($page)) {
+ $current = $page;
+ }
+
+ return $current;
+}
+
+/**
+ * Implementation of hook_panels_dashboard_blocks().
+ *
+ * Adds page information to the Panels dashboard.
+ */
+function page_manager_panels_dashboard_blocks(&$vars) {
+ $vars['links']['page_manager'] = array(
+ 'weight' => -100,
+ 'title' => l(t('Panel page'), 'admin/structure/pages/add'),
+ 'description' => t('Panel pages can be used as landing pages. They have a URL path, accept arguments and can have menu entries.'),
+ );
+
+ module_load_include('inc', 'page_manager', 'page_manager.admin');
+ $tasks = page_manager_get_tasks_by_type('page');
+ $pages = array('operations' => array());
+
+ page_manager_get_pages($tasks, $pages);
+ $count = 0;
+ $rows = array();
+ foreach ($pages['rows'] as $id => $info) {
+ $rows[] = array(
+ 'data' => array(
+ $info['data']['title'],
+ $info['data']['operations'],
+ ),
+ 'class' => $info['class'],
+ );
+
+ // Only show 10.
+ if (++$count >= 10) {
+ break;
+ }
+ }
+
+ $vars['blocks']['page_manager'] = array(
+ 'weight' => -100,
+ 'title' => t('Manage pages'),
+ 'link' => l(t('Go to list'), 'admin/structure/pages'),
+ 'content' => theme('table', array('header' => array(), 'rows' => $rows, 'attributes' => array('class' => 'panels-manage'))),
+ 'class' => 'dashboard-pages',
+ 'section' => 'right',
+ );
+}
+
+/**
+ * Implement pseudo-hook to fetch addressable content.
+ *
+ * For Page Manager, the address will be an array. The first
+ * element will be the $task and the second element will be the
+ * $task_handler. The third elements will be the arguments
+ * provided.
+ */
+function page_manager_addressable_content($address, $type) {
+ if (count($address) < 3) {
+ return;
+ }
+
+ $task_name = array_shift($address);
+ $subtask_name = array_shift($address);
+ $handler_name = array_shift($address);
+ if ($address) {
+ $arguments = array_shift($address);
+ }
+
+ // Since $arguments is an array of arbitrary size, we need to implode it:
+ if (!empty($arguments)) {
+ // The only choices we have for separators since :: is already
+ // used involve ., - or _. Since - and _ are more common than .
+ // in URLs, let's try .. as an argument separator.
+ $arguments = explode('..', $arguments);
+ }
+ else {
+ // implode does not return an empty array on an empty
+ // string so do it specifically.
+ $arguments = array();
+ }
+
+ $task = page_manager_get_task($task_name);
+ if (!$task) {
+ return;
+ }
+
+ $handler = page_manager_load_task_handler($task, $subtask_name, $handler_name);
+ if (!$handler) {
+ return;
+ }
+
+ $handler_plugin = page_manager_get_task_handler($handler->handler);
+ if (!$handler_plugin) {
+ return;
+ }
+
+ // Load the contexts for the task.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, $subtask_name, $arguments);
+
+ // With contexts loaded, ensure the task is accessible. Tasks without a callback
+ // are automatically accessible.
+ $function = ctools_plugin_get_function($task, 'access callback');
+ if ($function && !$function($task, $subtask_name, $contexts)) {
+ return;
+ }
+
+ $function = ctools_plugin_get_function($handler_plugin, 'addressable callback');
+ if ($function) {
+ return $function($task, $subtask_name, $handler, $address, $contexts, $arguments, $type);
+ }
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/cache/page_manager_context.inc b/sites/all/modules/ctools/page_manager/plugins/cache/page_manager_context.inc
new file mode 100644
index 000000000..2f01b5603
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/cache/page_manager_context.inc
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * A page_manager cache indirection mechanism that just attaches context
+ * caching to the larger page cache.
+ */
+
+$plugin = array(
+ // cache plugins are the rare plugin types that have no real UI but
+ // we're providing a title just in case.
+ 'title' => t('Page manager context'),
+ 'cache get' => 'page_manager_cache_page_manager_context_cache_get',
+ 'cache set' => 'page_manager_cache_page_manager_context_cache_set',
+ 'cache finalize' => 'page_manager_cache_page_manager_context_cache_finalize',
+
+ // We don't support a clear because the general uses of clear have no effect
+ // on us.
+);
+
+function page_manager_cache_page_manager_context_cache_get($data, $key) {
+ $page = page_manager_get_page_cache($data);
+ if ($page) {
+ if (!empty($page->context_cache[$key])) {
+ return $page->context_cache[$key];
+ }
+ else {
+ ctools_include('context-task-handler');
+ if ($key == 'temp') {
+ $handler = $page->new_handler;
+ }
+ else {
+ $handler = $page->handlers[$key];
+ }
+ return ctools_context_handler_get_task_object($page->task, $page->subtask, $handler);
+ }
+ }
+}
+
+function page_manager_cache_page_manager_context_cache_set($data, $key, $object) {
+ $page = page_manager_get_page_cache($data);
+ if ($page) {
+ $page->context_cache[$key] = $object;
+ return page_manager_set_page_cache($page);
+ }
+}
+
+/**
+ * Copy temporary data from the page manager cache
+ */
+function page_manager_cache_page_manager_context_cache_finalize($data, $key, $object) {
+ // Statically cached so there shouldn't be any worries. It's an object so
+ // referencing ensures that we'll get the right one.
+ $page = page_manager_get_page_cache($data);
+ if ($page) {
+ if ($key == 'temp') {
+ $handler = $page->new_handler;
+ }
+ else {
+ $handler = $page->handlers[$key];
+ }
+ $handler->conf['contexts'] = $object->contexts;
+ $handler->conf['relationships'] = $object->relationships;
+
+ if (isset($page->context_cache[$key])) {
+ unset($page->context_cache[$key]);
+ }
+ return page_manager_set_page_cache($page);
+ }
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/task_handlers/http_response.inc b/sites/all/modules/ctools/page_manager/plugins/task_handlers/http_response.inc
new file mode 100644
index 000000000..c4eba8e20
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/task_handlers/http_response.inc
@@ -0,0 +1,332 @@
+<?php
+
+/**
+ * @file
+ *
+ * This is the task handler plugin to handle generating 403, 404 and 301 response codes.
+ */
+
+// Plugin definition
+$plugin = array(
+ // is a 'context' handler type, meaning it supports the API of the
+ // context handlers provided by ctools context plugins.
+ 'handler type' => 'context',
+ 'visible' => TRUE, // may be added up front.
+
+ // Administrative fields.
+ 'title' => t('HTTP response code'),
+ 'admin summary' => 'page_manager_http_response_admin_summary',
+ 'admin title' => 'page_manager_http_response_title',
+ 'operations' => array(
+ 'settings' => array(
+ 'title' => t('General'),
+ 'description' => t('Change general settings for this variant.'),
+ 'form' => 'page_manager_http_response_edit_settings',
+ ),
+ 'criteria' => array(
+ 'title' => t('Selection rules'),
+ 'description' => t('Control the criteria used to decide whether or not this variant is used.'),
+ 'ajax' => FALSE,
+ 'form' => array(
+ 'order' => array(
+ 'form' => t('Selection rules'),
+ ),
+ 'forms' => array(
+ 'form' => array(
+ 'include' => drupal_get_path('module', 'ctools') . '/includes/context-task-handler.inc',
+ 'form id' => 'ctools_context_handler_edit_criteria',
+ ),
+ ),
+ ),
+ ),
+ 'context' => array(
+ 'title' => t('Contexts'),
+ 'ajax' => FALSE,
+ 'description' => t('Add additional context objects to this variant that can be used by the content.'),
+ 'form' => array(
+ 'order' => array(
+ 'form' => t('Context'),
+ ),
+ 'forms' => array(
+ 'form' => array(
+ 'include' => drupal_get_path('module', 'ctools') . '/includes/context-task-handler.inc',
+ 'form id' => 'ctools_context_handler_edit_context',
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ // Callback to render the data.
+ 'render' => 'page_manager_http_response_render',
+
+ 'add features' => array(
+ 'criteria' => t('Selection rules'),
+ 'context' => t('Contexts'),
+ ),
+ // Where to go when finished.
+ 'add finish' => 'settings',
+
+ 'required forms' => array(
+ 'settings' => t('Panel settings'),
+ ),
+
+ 'edit forms' => array(
+ 'criteria' => t('Selection rules'),
+ 'settings' => t('General'),
+ 'context' => t('Contexts'),
+ ),
+ 'forms' => array(
+ 'settings' => array(
+ 'form id' => 'page_manager_http_response_edit_settings',
+ ),
+ 'context' => array(
+ 'include' => drupal_get_path('module', 'ctools') . '/includes/context-task-handler.inc',
+ 'form id' => 'ctools_context_handler_edit_context',
+ ),
+ 'criteria' => array(
+ 'include' => drupal_get_path('module', 'ctools') . '/includes/context-task-handler.inc',
+ 'form id' => 'ctools_context_handler_edit_criteria',
+ ),
+ ),
+ 'default conf' => array(
+ 'title' => t('HTTP response code'),
+ 'contexts' => array(),
+ 'relationships' => array(),
+ 'code' => '404',
+ 'destination' => '',
+ ),
+);
+
+/**
+ * Provide a list of the response codes we support.
+ *
+ * Abstracted so it can be more readily used both on input and output.
+ */
+function page_manager_http_response_codes() {
+ return array(
+ 403 => t('403 Access denied'),
+ 404 => t('404 Page not found'),
+ 410 => t('410 Gone'),
+ 301 => t('301 Redirect'),
+ );
+}
+
+function page_manager_http_response_admin_summary($handler, $task, $subtask, $page, $show_title = TRUE) {
+ $task_name = page_manager_make_task_name($task['name'], $subtask['name']);
+ $output = '';
+
+ ctools_include('context');
+ ctools_include('context-task-handler');
+
+ // Get the operations
+ $operations = page_manager_get_operations($page);
+
+ // Get operations for just this handler.
+ $operations = $operations['handlers']['children'][$handler->name]['children']['actions']['children'];
+ $args = array('handlers', $handler->name, 'actions');
+ $rendered_operations = page_manager_render_operations($page, $operations, array(), array('class' => array('actions')), 'actions', $args);
+
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $object = ctools_context_handler_get_task_object($task, $subtask, $handler);
+ $context = ctools_context_load_contexts($object, TRUE);
+
+ $access = ctools_access_group_summary(!empty($handler->conf['access']) ? $handler->conf['access'] : array(), $context);
+ if ($access) {
+ $access = t('This panel will be selected if @conditions.', array('@conditions' => $access));
+ }
+ else {
+ $access = t('This panel will always be selected.');
+ }
+
+ $rows = array();
+
+ $type = $handler->type == t('Default') ? t('In code') : $handler->type;
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Storage')),
+ array('class' => array('page-summary-data'), 'data' => $type),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ if (!empty($handler->disabled)) {
+ $link = l(t('Enable'), page_manager_edit_url($task_name, array('handlers', $handler->name, 'actions', 'enable')));
+ $text = t('Disabled');
+ }
+ else {
+ $link = l(t('Disable'), page_manager_edit_url($task_name, array('handlers', $handler->name, 'actions', 'disable')));
+ $text = t('Enabled');
+ }
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Status')),
+ array('class' => array('page-summary-data'), 'data' => $text),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ $link = l(t('Edit'), page_manager_edit_url($task_name, array('handlers', $handler->name, 'criteria')));
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Selection rule')),
+ array('class' => array('page-summary-data'), 'data' => $access),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ $link = l(t('Edit'), page_manager_edit_url($task_name, array('handlers', $handler->name, 'settings')));
+ $codes = page_manager_http_response_codes();
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Response code')),
+ array('class' => array('page-summary-data'), 'data' => $codes[$handler->conf['code']]),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ $info = theme('table', array('header' => array(), 'rows' => $rows, 'attributes' => array('class' => array('page-manager-handler-summary'))));
+
+ $title = $handler->conf['title'];
+ if ($title != t('Panel')) {
+ $title = t('Panel: @title', array('@title' => $title));
+ }
+
+ $output .= '<div class="clearfix">';
+ if ($show_title) {
+ $output .= '<div class="handler-title clearfix">';
+ $output .= '<div class="actions handler-actions">' . $rendered_operations['actions'] . '</div>';
+ $output .= '<span class="title-label">' . $title . '</span>';
+ }
+
+ $output .= '</div>';
+ $output .= $info;
+ $output .= '</div>';
+
+ return $output;
+}
+
+/**
+ * Set up a title for the panel based upon the selection rules.
+ */
+function page_manager_http_response_title($handler, $task, $subtask) {
+ if (isset($handler->conf['title'])) {
+ return check_plain($handler->conf['title']);
+ }
+ else {
+ return t('HTTP response code');
+ }
+}
+
+/**
+ * General settings for the panel
+ */
+function page_manager_http_response_edit_settings($form, &$form_state) {
+ ctools_include('page_manager.admin', 'page_manager', '');
+ ctools_include('export', 'ctools');
+
+ $conf = $form_state['handler']->conf;
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#default_value' => $conf['title'],
+ '#title' => t('Administrative title'),
+ '#description' => t('Administrative title of this variant.'),
+ );
+
+ $name = isset($conf['name']) ? $conf['name'] : FALSE;
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#title' => t('Machine name'),
+ '#required' => FALSE,
+ '#default_value' => $name,
+ '#description' => t("A unique machine-readable name for this variant. It must only contain lowercase letters, numbers, and underscores. This name will be used when exporting the variant. If left empty the variant's name will be used instead."),
+ '#size' => 32,
+ '#maxlength' => 32,
+ '#machine_name' => array(
+ 'exists' => 'page_manager_handler_check_machine_name',
+ 'source' => array('title'),
+ ),
+ '#field_prefix' => '<span dir="ltr">' . $form_state['task_name'] . '__',
+ '#field_suffix' => '</span>&lrm;',
+ );
+
+ $form['code'] = array(
+ '#title' => t('Response code'),
+ '#type' => 'select',
+ '#options' => page_manager_http_response_codes(),
+ '#default_value' => $conf['code'],
+ );
+
+ ctools_include('dependent');
+ $form['destination'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Redirect destination'),
+ '#default_value' => $conf['destination'],
+ '#dependency' => array('edit-code' => array(301)),
+ '#description' => t('Enter the path to redirect to. You may use keyword substitutions from contexts. You can use external urls (http://www.example.com/foo) or internal urls (node/1).'),
+ );
+
+ return $form;
+}
+
+function page_manager_http_response_edit_settings_submit($form, &$form_state) {
+ $machine_name = $form_state['handler']->name;
+ $name = $form_state['task_name'] . '__' . $form_state['values']['name'];
+
+ // If new name doesn't equal machine name, we need to update and redirect.
+ if ($machine_name !== $name) {
+ $form_state['handler']->name = $name;
+ // If there's a trail, we need to replace it for redirection.
+ if (isset($form_state['trail'])) {
+ $form_state['new trail'] = $form_state['trail'];
+ $delta = array_search($machine_name, $form_state['new trail']);
+ $form_state['new trail'][$delta] = $name;
+ }
+ // If handler id is set, replace it.
+ if ($form_state['handler_id']) {
+ $form_state['handler_id'] = $name;
+ }
+ // If we're defining a new custom handler, move page handler to new name.
+ if (isset($form_state['page']->handlers[$machine_name]) && isset($form_state['page']->handler_info[$machine_name])) {
+ $form_state['page']->handlers[$name] = $form_state['page']->handlers[$machine_name];
+ unset($form_state['page']->handlers[$machine_name]);
+ $form_state['page']->handler_info[$name] = $form_state['page']->handler_info[$machine_name];
+ unset($form_state['page']->handler_info[$machine_name]);
+ }
+ }
+
+ $form_state['handler']->conf['title'] = $form_state['values']['title'];
+ $form_state['handler']->conf['name'] = $form_state['values']['name'];
+ $form_state['handler']->conf['code'] = $form_state['values']['code'];
+ $form_state['handler']->conf['destination'] = $form_state['values']['destination'];
+}
+
+function page_manager_http_response_render($handler, $base_contexts, $args, $test = TRUE) {
+ // Go through arguments and see if they match.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+
+ // Add my contexts
+ $contexts = ctools_context_handler_get_handler_contexts($base_contexts, $handler);
+
+ // Test.
+ if ($test && !ctools_context_handler_select($handler, $contexts)) {
+ return;
+ }
+
+ if (isset($handler->handler)) {
+ ctools_context_handler_pre_render($handler, $contexts, $args);
+ }
+
+ $info['response code'] = $handler->conf['code'];
+ if ($info['response code'] == 301) {
+ $path = ctools_context_keyword_substitute($handler->conf['destination'], array(), $contexts);
+ $url = parse_url($path);
+ if (isset($url['query'])) {
+ $path = strtr($path, array('?' . $url['query'] => ''));
+ $info['query'] = drupal_get_query_array($url['query']);
+ }
+ if (isset($url['fragment'])) {
+ $path = strtr($path, array('#' . $url['fragment'] => ''));
+ $info['fragment'] = $url['fragment'];
+ }
+
+ $info['destination'] = rtrim($path, '?');
+ }
+
+ return $info;
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/blog.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/blog.inc
new file mode 100644
index 000000000..bab2dd28d
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/blog.inc
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_blog_page_manager_tasks() {
+ if (!module_exists('blog')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('All blogs'),
+ 'admin title' => t('All blogs'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for the all blogs at <em>/blog</em>. If no variant is selected, the default Drupal most recent blog posts will be shown.'),
+ 'admin path' => 'blog',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu alter' => 'page_manager_blog_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_blog_disabled', TRUE),
+ 'enable callback' => 'page_manager_blog_enable',
+ 'access callback' => 'page_manager_blog_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_blog_page_manager_tasks().
+ *
+ * Alter the node edit input so that node edit comes to us rather than the
+ * normal node edit process.
+ */
+function page_manager_blog_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_blog_disabled', TRUE)) {
+ return;
+ }
+
+ $callback = $items['blog']['page callback'];
+ // Override the node edit handler for our purpose.
+ if ($callback == 'blog_page_last' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['blog']['page callback'] = 'page_manager_blog';
+ $items['blog']['file path'] = $task['path'];
+ $items['blog']['file'] = $task['file'];
+ }
+ else {
+ variable_set('page_manager_blog_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_blog'])) {
+ drupal_set_message(t('Page manager module is unable to enable blog because some other module already has overridden with %callback.', array('%callback' => $callback)), 'warning');
+ }
+ return;
+ }
+
+}
+
+/**
+ * Entry point for our overridden node edit.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * node edit, which is node_page_edit().
+ */
+function page_manager_blog() {
+ // Load my task plugin
+ $task = page_manager_get_task('blog');
+
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $output = ctools_context_handler_render($task, '', array(), array());
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ module_load_include('inc', 'blog', 'blog.pages');
+ $function = 'blog_page_last';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('blog')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ return $function();
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_blog_enable($cache, $status) {
+ variable_set('page_manager_blog_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_blog'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_blog_access_check($task, $subtask_id, $contexts) {
+ return user_access('access content');
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/blog_user.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/blog_user.inc
new file mode 100644
index 000000000..351e4de09
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/blog_user.inc
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_blog_user_page_manager_tasks() {
+ if (!module_exists('blog')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+ 'title' => t('User blog'),
+ 'admin title' => t('User blog'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying user blogs at <em>blog/%user</em>. If no variant is selected, the default Drupal user blog will be used.'),
+ 'admin path' => 'blog/%user',
+
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_blog_user_task_admin',
+
+ 'hook menu alter' => 'page_manager_blog_user_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context', // handler type -- misnamed
+ 'get arguments' => 'page_manager_blog_user_get_arguments',
+ 'get context placeholders' => 'page_manager_blog_user_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_blog_user_disabled', TRUE),
+ 'enable callback' => 'page_manager_blog_user_enable',
+ 'access callback' => 'page_manager_blog_user_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_blog_user_page_manager_tasks().
+ *
+ * Alter the user view input so that user view comes to us rather than the
+ * normal user view process.
+ */
+function page_manager_blog_user_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_blog_user_disabled', TRUE)) {
+ return;
+ }
+
+ // Override the user view handler for our purpose.
+ if ($items['blog/%user_uid_optional']['page callback'] == 'blog_page_user' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['blog/%user_uid_optional']['page callback'] = 'page_manager_blog_user';
+ $items['blog/%user_uid_optional']['file path'] = $task['path'];
+ $items['blog/%user_uid_optional']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_blog_user_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_blog_user'])) {
+ drupal_set_message(t('Page manager module is unable to enable blog/%user because some other module already has overridden with %callback.', array('%callback' => $items['blog/%user']['page callback'])), 'error');
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden user view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * user view, which is user_page_view().
+ */
+function page_manager_blog_user($account) {
+ // Load my task plugin:
+ $task = page_manager_get_task('blog_user');
+
+ // Load the account into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($account));
+
+ $output = ctools_context_handler_render($task, '', $contexts, array($account->uid));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ module_load_include('inc', 'blog', 'blog.pages');
+ $function = 'blog_page_user';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('blog_user')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ return $function($account);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_blog_user_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'user',
+ 'identifier' => t('User being viewed'),
+ 'id' => 1,
+ 'name' => 'uid',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_blog_user_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_blog_user_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_blog_user_enable($cache, $status) {
+ variable_set('page_manager_blog_user_disabled', $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_blog_user'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_blog_user_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return blog_page_user_access($context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/comment_reply.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/comment_reply.inc
new file mode 100644
index 000000000..ffbafe462
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/comment_reply.inc
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_comment_reply_page_manager_tasks() {
+ if (!module_exists('comment')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('Comment Reply page'),
+ 'admin title' => t('Comment Reply page'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for the site contact page at <em>/contact</em>. If no variant is selected, the default Drupal contact form will be used.'),
+ 'admin path' => 'comment/reply/%node',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu alter' => 'page_manager_comment_reply_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+ 'get arguments' => 'page_manager_comment_reply_get_arguments',
+ 'get context placeholders' => 'page_manager_comment_reply_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_comment_reply_disabled', TRUE),
+ 'enable callback' => 'page_manager_comment_reply_enable',
+ 'access callback' => 'page_manager_comment_reply_check',
+ );
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_comment_reply_enable($cache, $status) {
+ variable_set('page_manager_comment_reply_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_comment_reply'] = TRUE;
+ }
+}
+
+
+/**
+ * Entry point for our overridden comment.
+ *
+ */
+function page_manager_comment_reply_page($node, $pid = NULL){
+ // Load my task plugin
+ $task = page_manager_get_task('comment_reply');
+
+ // Load the node into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($node, $pid));
+
+ if (array_key_exists('argument_cid_3', $contexts) && $contexts['argument_cid_3']->data->nid != $node->nid) {
+ // Attempting to reply to a comment not belonging to the current nid.
+ drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
+ drupal_goto("node/$node->nid");
+ }
+
+ $output = ctools_context_handler_render($task, '', $contexts, array($node, $pid));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ $function = 'comment_reply';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('comment_reply')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ module_load_include('inc', 'comment', 'comment.pages');
+ return $function($node, $pid);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_comment_reply_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'node',
+ 'identifier' => t('Node being commented on'),
+ 'id' => 2,
+ 'name' => 'entity_id:node',
+ 'settings' => array(),
+ ),
+ array(
+ 'keyword' => 'comment',
+ 'identifier' => t('Comment being replied to'),
+ 'id' => 3,
+ 'name' => 'entity_id:comment',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_comment_reply_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_comment_reply_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback defined by page_manager_node_view_page_manager_tasks().
+ *
+ * Alter the node view input so that node view comes to us rather than the
+ * normal node view process.
+ */
+function page_manager_comment_reply_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_comment_reply_disabled', TRUE)) {
+ return;
+ }
+ // Override the node view handler for our purpose.
+ $callback = $items['comment/reply/%node']['page callback'];
+ if ($callback == 'comment_reply' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['comment/reply/%node']['page callback'] = 'page_manager_comment_reply_page';
+ $items['comment/reply/%node']['file path'] = $task['path'];
+ $items['comment/reply/%node']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_comment_reply_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_comment_reply'])) {
+ drupal_set_message(t('Page manager module is unable to enable comment/reply/%node because some other module already has overridden with %callback.', array('%callback' => $callback)), 'error');
+ }
+ }
+
+ // @todo override node revision handler as well?
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_comment_reply_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return TRUE;
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/contact_site.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/contact_site.inc
new file mode 100644
index 000000000..f8718697c
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/contact_site.inc
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_contact_site_page_manager_tasks() {
+ if (!module_exists('contact')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('Site contact page'),
+ 'admin title' => t('Site contact page'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for the site contact page at <em>/contact</em>. If no variant is selected, the default Drupal contact form will be used.'),
+ 'admin path' => 'contact',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu alter' => 'page_manager_contact_site_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_contact_site_disabled', TRUE),
+ 'enable callback' => 'page_manager_contact_site_enable',
+ 'access callback' => 'page_manager_contact_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_contact_site_page_manager_tasks().
+ *
+ * Alter the node edit input so that node edit comes to us rather than the
+ * normal node edit process.
+ */
+function page_manager_contact_site_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_contact_site_disabled', TRUE)) {
+ return;
+ }
+
+ $callback = $items['contact']['page callback'];
+ if ($callback == 'drupal_get_form') {
+ $callback = $items['contact']['page arguments'][0];
+ }
+
+ // Override the node edit handler for our purpose.
+ if ($callback == 'contact_site_form' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['contact']['page callback'] = 'page_manager_contact_site';
+ $items['contact']['file path'] = $task['path'];
+ $items['contact']['file'] = $task['file'];
+ }
+ else {
+ variable_set('page_manager_contact_site_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_contact_site'])) {
+ drupal_set_message(t('Page manager module is unable to enable contact because some other module already has overridden with %callback.', array('%callback' => $callback)), 'warning');
+ }
+ return;
+ }
+
+}
+
+/**
+ * Entry point for our overridden site contact.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * node edit, which is node_page_edit().
+ */
+function page_manager_contact_site() {
+ // Load my task plugin
+ $task = page_manager_get_task('contact_site');
+
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $output = ctools_context_handler_render($task, '', array(), array());
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ module_load_include('inc', 'contact', 'contact.pages');
+ $function = 'contact_site_form';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('contact_site')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ if ($function == 'contact_site_form') {
+ return drupal_get_form($function);
+ }
+
+ return $function();
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_contact_site_enable($cache, $status) {
+ variable_set('page_manager_contact_site_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_contact_site'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_contact_access_check($task, $subtask_id, $contexts) {
+ return user_access('access site-wide contact form');
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/contact_user.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/contact_user.inc
new file mode 100644
index 000000000..5c868161b
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/contact_user.inc
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_contact_user_page_manager_tasks() {
+ if (!module_exists('contact')) {
+ return;
+ }
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+ 'title' => t('User contact'),
+ 'admin title' => t('User contact'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying the user contact form at <em>user/%user/contact</em>. If no variant is selected, the default Drupal user contact form will be used.'),
+ 'admin path' => 'user/%user/contact',
+
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_contact_user_task_admin',
+
+ 'hook menu alter' => 'page_manager_contact_user_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context', // handler type -- misnamed
+ 'get arguments' => 'page_manager_contact_user_get_arguments',
+ 'get context placeholders' => 'page_manager_contact_user_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_contact_user_disabled', TRUE),
+ 'enable callback' => 'page_manager_contact_user_enable',
+ 'access callback' => 'page_manager_contact_user_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_contact_user_page_manager_tasks().
+ *
+ * Alter the user view input so that user view comes to us rather than the
+ * normal user view process.
+ */
+function page_manager_contact_user_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_contact_user_disabled', TRUE)) {
+ return;
+ }
+ $callback = $items['user/%user/contact']['page callback'];
+ if ($callback == 'drupal_get_form') {
+ $callback = $items['user/%user/contact']['page arguments'][0];
+ }
+
+ // Override the user view handler for our purpose.
+ if ($callback == 'contact_personal_form' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['user/%user/contact']['page callback'] = 'page_manager_contact_user';
+ $items['user/%user/contact']['file path'] = $task['path'];
+ $items['user/%user/contact']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_contact_user_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_contact_user'])) {
+ drupal_set_message(t('Page manager module is unable to enable user/%user/contact because some other module already has overridden with %callback.', array('%callback' => $callback)), 'error');
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden user view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * user view, which is user_page_view().
+ */
+function page_manager_contact_user($form_id, $account) {
+ // Load my task plugin:
+ $task = page_manager_get_task('contact_user');
+
+ // Load the account into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($account));
+
+ $output = ctools_context_handler_render($task, '', $contexts, array($account->uid));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ module_load_include('inc', 'contact', 'contact.pages');
+ $function = 'contact_personal_form';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('contact_user')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ return drupal_get_form($function, $account);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_contact_user_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'user',
+ 'identifier' => t('User being viewed'),
+ 'id' => 1,
+ 'name' => 'uid',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_contact_user_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_contact_user_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_contact_user_enable($cache, $status) {
+ variable_set('page_manager_contact_user_disabled', $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_contact_user'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_blog_contact_user_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return _contact_personal_tab_access($context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/node_edit.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/node_edit.inc
new file mode 100644
index 000000000..61ef13ac4
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/node_edit.inc
@@ -0,0 +1,185 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_node_edit_page_manager_tasks() {
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('Node add/edit form'),
+ 'admin title' => t('Node add/edit form'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for adding or edit nodes at <em>node/%node/edit</em> and <em>node/add/%node_type</em>. If you add variants, you may use selection criteria such as node type or language or user access to provide different edit forms for nodes. If no variant is selected, the default Drupal node edit will be used.'),
+ 'admin path' => 'node/%node/edit',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu' => 'page_manager_node_edit_menu',
+ 'hook menu alter' => 'page_manager_node_edit_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+ 'get arguments' => 'page_manager_node_edit_get_arguments',
+ 'get context placeholders' => 'page_manager_node_edit_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_node_edit_disabled', TRUE),
+ 'enable callback' => 'page_manager_node_edit_enable',
+ 'access callback' => 'page_manager_node_edit_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_node_edit_page_manager_tasks().
+ *
+ * Alter the node edit input so that node edit comes to us rather than the
+ * normal node edit process.
+ */
+function page_manager_node_edit_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_node_edit_disabled', TRUE)) {
+ return;
+ }
+
+ $callback = $items['node/%node/edit']['page callback'];
+ // Override the node edit handler for our purpose.
+ if ($callback == 'node_page_edit' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['node/%node/edit']['page callback'] = 'page_manager_node_edit';
+ $items['node/%node/edit']['file path'] = $task['path'];
+ $items['node/%node/edit']['file'] = $task['file'];
+ }
+ else {
+ variable_set('page_manager_node_edit_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_node_edit'])) {
+ drupal_set_message(t('Page manager module is unable to enable node/%node/edit because some other module already has overridden with %callback.', array('%callback' => $callback)), 'warning');
+ }
+ return;
+ }
+
+ // Also catch node/add handling:
+ foreach (node_type_get_types() as $type) {
+ $path = 'node/add/' . str_replace('_', '-', $type->type);
+ if ($items[$path]['page callback'] != 'node_add') {
+ if (!empty($GLOBALS['page_manager_enabling_node_edit'])) {
+ drupal_set_message(t('Page manager module is unable to override @path because some other module already has overridden with %callback. Node edit will be enabled but that edit path will not be overridden.', array('@path' => $path, '%callback' => $items[$path]['page callback'])), 'warning');
+ }
+ continue;
+ }
+
+ $items[$path]['page callback'] = 'page_manager_node_add';
+ $items[$path]['file path'] = $task['path'];
+ $items[$path]['file'] = $task['file'];
+ // Why str_replace things back?
+ $items[$path]['page arguments'] = array($type->type);
+ }
+}
+
+/**
+ * Entry point for our overridden node edit.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * node edit, which is node_page_edit().
+ */
+function page_manager_node_edit($node) {
+ // Load my task plugin
+ $task = page_manager_get_task('node_edit');
+
+ // Load the node into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($node));
+
+ $arg = array(isset($node->nid) ? $node->nid : $node->type);
+ $output = ctools_context_handler_render($task, '', $contexts, $arg);
+ if ($output === FALSE) {
+ // Fall back!
+ // We've already built the form with the context, so we can't build it again, or
+ // form_clean_id will mess up our ids. But we don't really need to, either:
+ $context = reset($contexts);
+ $output = $context->form;
+ }
+
+ return $output;
+}
+
+/**
+ * Callback to handle the process of adding a node.
+ *
+ * This creates a basic $node and passes that off to page_manager_node_edit().
+ * It is modelled after Drupal's node_add() function.
+ *
+ * Unlike node_add() we do not need to check node_access because that was
+ * already checked by the menu system.
+ */
+function page_manager_node_add($type) {
+ global $user;
+
+ $types = node_type_get_types();
+
+ // Initialize settings:
+ $node = (object) array(
+ 'uid' => $user->uid,
+ 'name' => (isset($user->name) ? $user->name : ''),
+ 'type' => $type,
+ 'language' => LANGUAGE_NONE,
+ );
+
+ drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), PASS_THROUGH);
+ return page_manager_node_edit($node);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node edit and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_node_edit_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'node',
+ 'identifier' => t('Node being edited'),
+ 'id' => 1,
+ 'name' => 'node_edit',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_node_edit_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_node_edit_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_node_edit_enable($cache, $status) {
+ variable_set('page_manager_node_edit_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_node_edit'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_node_edit_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return node_access('update', $context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/node_view.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/node_view.inc
new file mode 100644
index 000000000..89a291287
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/node_view.inc
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * @file
+ * Handle the 'node view' override task.
+ *
+ * This plugin overrides node/%node and reroutes it to the page manager, where
+ * a list of tasks can be used to service this request based upon criteria
+ * supplied by access plugins.
+ */
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_node_view_page_manager_tasks() {
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('Node template'),
+
+ 'admin title' => t('Node template'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying nodes at <em>node/%node</em>. If you add variants, you may use selection criteria such as node type or language or user access to provide different views of nodes. If no variant is selected, the default Drupal node view will be used. This page only affects nodes viewed as pages, it will not affect nodes viewed in lists or at other locations. Also please note that if you are using pathauto, aliases may make a node to be somewhere else, but as far as Drupal is concerned, they are still at node/%node.'),
+ 'admin path' => 'node/%node',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu' => 'page_manager_node_view_menu',
+ 'hook menu alter' => 'page_manager_node_view_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+ 'get arguments' => 'page_manager_node_view_get_arguments',
+ 'get context placeholders' => 'page_manager_node_view_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_node_view_disabled', TRUE),
+ 'enable callback' => 'page_manager_node_view_enable',
+ 'access callback' => 'page_manager_node_view_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_node_view_page_manager_tasks().
+ *
+ * Alter the node view input so that node view comes to us rather than the
+ * normal node view process.
+ */
+function page_manager_node_view_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_node_view_disabled', TRUE)) {
+ return;
+ }
+
+ // Override the node view handler for our purpose.
+ $callback = $items['node/%node']['page callback'];
+ if ($callback == 'node_page_view' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['node/%node']['page callback'] = 'page_manager_node_view_page';
+ $items['node/%node']['file path'] = $task['path'];
+ $items['node/%node']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_node_view_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_node_view'])) {
+ drupal_set_message(t('Page manager module is unable to enable node/%node because some other module already has overridden with %callback.', array('%callback' => $callback)), 'error');
+ }
+ }
+
+ // @todo override node revision handler as well?
+}
+
+/**
+ * Entry point for our overridden node view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * node view, which is node_page_view().
+ */
+function page_manager_node_view_page($node) {
+ // Load my task plugin
+ $task = page_manager_get_task('node_view');
+
+ // Load the node into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+
+ // Load all contexts.
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($node));
+
+ // Build the full output using the configured CTools plugin.
+ $output = ctools_context_handler_render($task, '', $contexts, array($node->nid));
+ if ($output !== FALSE) {
+ node_tag_new($node);
+ return $output;
+ }
+
+ // Try loading an override plugin.
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('node_view')) && function_exists($rc)) {
+ return $rc($node);
+ }
+ }
+
+ // Prepare the node to be displayed so all of the regular hooks are triggered.
+ $default_output = node_page_view($node);
+
+ // Otherwise, fall back to the default output generated by node_page_view().
+ return $default_output;
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_node_view_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'node',
+ 'identifier' => t('Node being viewed'),
+ 'id' => 1,
+ 'name' => 'entity_id:node',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_node_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_node_view_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_node_view_enable($cache, $status) {
+ variable_set('page_manager_node_view_disabled', $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_node_view'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_node_view_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return node_access('view', $context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/page.admin.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/page.admin.inc
new file mode 100644
index 000000000..97bd37bc4
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/page.admin.inc
@@ -0,0 +1,1521 @@
+<?php
+
+/**
+ * @file
+ * Administrative functions for the page subtasks.
+ *
+ * These are attached to the menu system in page.inc via the hook_menu
+ * delegation. They are included here so that this code is loaded
+ * only when needed.
+ */
+
+/**
+ * Delegated implementation of hook_menu().
+ */
+function page_manager_page_menu(&$items, $task) {
+ // Set up access permissions.
+ $access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access';
+ $access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer page manager');
+
+ $base = array(
+ 'access callback' => $access_callback,
+ 'access arguments' => $access_arguments,
+ 'file' => 'plugins/tasks/page.admin.inc',
+ );
+
+ $items['admin/structure/pages/add'] = array(
+ 'title' => 'Add custom page',
+ 'page callback' => 'page_manager_page_add_subtask',
+ 'page arguments' => array(),
+ 'type' => MENU_LOCAL_ACTION,
+ ) + $base;
+
+ $items['admin/structure/pages/import'] = array(
+ 'title' => 'Import page',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('page_manager_page_import_subtask', 'page'),
+ 'type' => MENU_LOCAL_ACTION,
+ ) + $base;
+ if ($access_callback == 'user_access') {
+ $items['admin/structure/pages/import']['access callback'] = 'ctools_access_multiperm';
+ $items['admin/structure/pages/import']['access arguments'][] = 'use ctools import';
+ }
+
+ // AJAX callbacks for argument modal.
+ $items['admin/structure/pages/argument'] = array(
+ 'page callback' => 'page_manager_page_subtask_argument_ajax',
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ // Add menu entries for each subtask
+ foreach (page_manager_page_load_all() as $subtask_id => $subtask) {
+ if (!empty($subtask->disabled)) {
+ continue;
+ }
+
+ if (!isset($subtask->access['type'])) {
+ $subtask->access['type'] = 'none';
+ }
+ if (!isset($subtask->access['settings'])) {
+ $subtask->access['settings'] = NULL;
+ }
+
+ $path = array();
+ $page_arguments = array((string) $subtask_id);
+ $access_arguments = array($subtask->access);
+ $load_arguments = array($subtask_id, '%index', '%map');
+
+ // Replace named placeholders with our own placeholder to load contexts.
+ $position = 0;
+
+ foreach (explode('/', $subtask->path) as $bit) {
+ // Remove things like double slashes completely.
+ if (!isset($bit) || $bit === '') {
+ continue;
+ }
+
+ if ($bit[0] == '%' && $bit != '%') {
+ $placeholder = '%pm_arg';
+
+ // Chop off that %.
+ $name = substr($bit, 1);
+
+ // Check to see if the argument plugin wants to use a different
+ // placholder. This will allow to_args.
+ if (!empty($subtask->arguments[$name])) {
+ ctools_include('context');
+ if (!empty($subtask->arguments[$name]['name'])) {
+ $plugin = ctools_get_argument($subtask->arguments[$name]['name']);
+ if (isset($plugin['path placeholder'])) {
+ if (function_exists($plugin['path placeholder'])) {
+ $placeholder = $plugin['path placeholder']($subtask->arguments[$name]);
+ }
+ else {
+ $placeholder = $plugin['path placeholder'];
+ }
+ }
+ }
+ }
+ // If an argument, swap it out with our argument loader and make sure
+ // the argument gets passed through to the page callback.
+ $path[] = $placeholder;
+ $page_arguments[] = $position;
+ $access_arguments[] = $position;
+ }
+ else if ($bit[0] != '!') {
+ $path[] = $bit;
+ }
+
+ // Increment position. We do it like this to skip empty items that
+ // could happen from erroneous paths like: this///that
+ $position++;
+ }
+
+ $menu_path = implode('/', $path);
+
+ $items[$menu_path] = page_manager_page_menu_item($task, $subtask->menu, $access_arguments, $page_arguments, $load_arguments);
+
+ // Add a parent menu item if one is configured.
+ if (isset($subtask->menu['type']) && $subtask->menu['type'] == 'default tab') {
+ array_pop($path);
+ $parent_path = implode('/', $path);
+ $items[$parent_path] = page_manager_page_menu_item($task, $subtask->menu['parent'], $access_arguments, $page_arguments, $load_arguments);
+ }
+ }
+}
+
+/**
+ * Create a menu item for page manager pages.
+ *
+ * @param $menu
+ * The configuration to use. It will contain a type, and depending on the
+ * type may also contain weight, title and name. These are presumed to have
+ * been configured from the UI.
+ * @param $access_arguments
+ * Arguments that go with ctools_access_menu; it should be loaded with
+ * the access plugin type, settings, and positions of any arguments that
+ * may produce contexts.
+ * @param $page_arguments
+ * This should be seeded with the subtask name for easy loading and like
+ * the access arguments above should contain positions of arguments so
+ * that the menu system passes contexts through.
+ * @param $load_arguments
+ * Arguments to send to the arg loader; should be the subtask id and '%index'.
+ */
+function page_manager_page_menu_item($task, $menu, $access_arguments, $page_arguments, $load_arguments) {
+ $item = array(
+ 'access callback' => 'ctools_access_menu',
+ 'access arguments' => $access_arguments,
+ 'page callback' => 'page_manager_page_execute',
+ 'page arguments' => $page_arguments,
+ 'load arguments' => $load_arguments,
+ 'file' => 'plugins/tasks/page.inc',
+ );
+
+ if (isset($menu['title'])) {
+ $item['title'] = $menu['title'];
+ }
+ if (isset($menu['weight'])) {
+ $item['weight'] = $menu['weight'];
+ }
+
+ if (empty($menu['type'])) {
+ $menu['type'] = 'none';
+ }
+
+ switch ($menu['type']) {
+ case 'none':
+ default:
+ $item['type'] = MENU_CALLBACK;
+ break;
+
+ case 'normal':
+ $item['type'] = MENU_NORMAL_ITEM;
+ // Insert item into the proper menu
+ $item['menu_name'] = $menu['name'];
+ break;
+
+ case 'tab':
+ $item['type'] = MENU_LOCAL_TASK;
+ break;
+
+ case 'action':
+ $item['type'] = MENU_LOCAL_ACTION;
+ break;
+
+ case 'default tab':
+ $item['type'] = MENU_DEFAULT_LOCAL_TASK;
+ break;
+ }
+
+ return $item;
+}
+
+/**
+ * Page callback to add a subtask.
+ */
+function page_manager_page_add_subtask($task_name = NULL, $step = NULL) {
+ ctools_include('context');
+ $task = page_manager_get_task('page');
+ $task_handler_plugins = page_manager_get_task_handler_plugins($task);
+ if (empty($task_handler_plugins)) {
+ drupal_set_message(t('There are currently no variants available and a page may not be added. Perhaps you need to install the Panels module to get a variant?'), 'error');
+ return ' ';
+ }
+
+ $form_info = array(
+ 'id' => 'page_manager_add_page',
+ 'show trail' => TRUE,
+ 'show back' => TRUE,
+ 'show return' => FALSE,
+ 'next callback' => 'page_manager_page_add_subtask_next',
+ 'finish callback' => 'page_manager_page_add_subtask_finish',
+ 'return callback' => 'page_manager_page_add_subtask_finish',
+ 'cancel callback' => 'page_manager_page_add_subtask_cancel',
+ 'add order' => array(
+ 'basic' => t('Basic settings'),
+ 'argument' => t('Argument settings'),
+ 'access' => t('Access control'),
+ 'menu' => t('Menu settings'),
+ ),
+ 'forms' => array(
+ 'basic' => array(
+ 'form id' => 'page_manager_page_form_basic',
+ ),
+ 'access' => array(
+ 'form id' => 'page_manager_page_form_access',
+ ),
+ 'menu' => array(
+ 'form id' => 'page_manager_page_form_menu',
+ ),
+ 'argument' => array(
+ 'form id' => 'page_manager_page_form_argument',
+ ),
+ ),
+ );
+
+ if ($task_name) {
+ $page = page_manager_get_page_cache($task_name);
+ if (empty($page)) {
+ return MENU_NOT_FOUND;
+ }
+
+ $form_info['path'] = "admin/structure/pages/add/$task_name/%step";
+ }
+ else {
+ $new_page = page_manager_page_new();
+ $new_page->name = NULL;
+
+ $page = new stdClass();
+ page_manager_page_new_page_cache($new_page, $page);
+ $form_info['path'] = 'admin/structure/pages/add/%task_name/%step';
+ }
+
+ if ($step && $step != 'basic') {
+ $handler_plugin = page_manager_get_task_handler($page->handler);
+
+ $form_info['forms'] += $handler_plugin['forms'];
+
+ if (isset($page->forms)) {
+ foreach ($page->forms as $id) {
+ if (isset($form_info['add order'][$id])) {
+ $form_info['order'][$id] = $form_info['add order'][$id];
+ }
+ else if (isset($handler_plugin['add features'][$id])) {
+ $form_info['order'][$id] = $handler_plugin['add features'][$id];
+ }
+ else if (isset($handler_plugin['required forms'][$id])) {
+ $form_info['order'][$id] = $handler_plugin['required forms'][$id];
+ }
+ }
+ }
+ else {
+ $form_info['order'] = $form_info['add order'];
+ }
+
+ // This means we just submitted our form from the default list
+ // of steps, which we've traded in for a newly generated list of
+ // steps above. We need to translate this 'next' step into what
+ // our questions determined would be next.
+ if ($step == 'next') {
+ $keys = array_keys($form_info['order']);
+ // get rid of 'basic' from the list of forms.
+ array_shift($keys);
+ $step = array_shift($keys);
+
+ // If $step == 'basic' at this point, we were not presented with any
+ // additional forms at all. Let's just save and go!
+ if ($step == 'basic') {
+ page_manager_save_page_cache($page);
+ // Redirect to the new page's task handler editor.
+ drupal_goto(page_manager_edit_url($page->task_name));
+ }
+ }
+ }
+ else {
+ $form_info['show trail'] = FALSE;
+ $form_info['order'] = array(
+ 'basic' => t('Basic settings'),
+ 'next' => t('A meaningless second page'),
+ );
+ }
+
+ ctools_include('wizard');
+ $form_state = array(
+ 'task' => $task,
+ 'subtask' => $page->subtask,
+ 'page' => &$page,
+ 'type' => 'add',
+ 'task_id' => 'page',
+ 'task_name' => $page->task_name,
+ 'creating' => TRUE,
+ );
+
+ if (!empty($page->handlers)) {
+ $keys = array_keys($page->handlers);
+ $key = array_shift($keys);
+ $form_state['handler'] = &$page->handlers[$key];
+ $form_state['handler_id'] = $key;
+ }
+
+ $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
+
+ if (!$output) {
+ // redirect.
+ drupal_redirect_form(array(), $form_state['redirect']);
+ }
+
+ return $output;
+}
+
+/**
+ * Callback generated when the add page process is finished.
+ */
+function page_manager_page_add_subtask_finish(&$form_state) {
+ $page = &$form_state['page'];
+ // Update the cache with changes.
+ page_manager_set_page_cache($page);
+
+ $handler = $form_state['handler'];
+ $handler_plugin = page_manager_get_task_handler($handler->handler);
+
+ // Redirect to the new page's task handler editor.
+ if (isset($handler_plugin['add finish'])) {
+ $form_state['redirect'] = page_manager_edit_url($page->task_name, array('handlers', $handler->name, $handler_plugin['add finish']));
+ }
+ else {
+ $form_state['redirect'] = page_manager_edit_url($page->task_name);
+ }
+ return;
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_page_add_subtask_next(&$form_state) {
+ if (empty($form_state['task_name']) || $form_state['task_name'] == 'page') {
+ // We may not have known the path to go next, because we didn't yet know the
+ // task name. This fixes that.
+ $form_state['form_info']['path'] = str_replace('%task_name', $form_state['page']->task_name, $form_state['form_info']['path']);
+
+ $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']);
+ }
+
+ // Update the cache with changes.
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * All we do here is clear the cache.
+ */
+function page_manager_page_add_subtask_cancel(&$form_state) {
+ // Wipe all our stored changes.
+ if (isset($form_state['page']->task_name)) {
+ page_manager_clear_page_cache($form_state['page']->task_name);
+ }
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_form_basic($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $task = $form_state['task'];
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
+ '#default_value' => $page->admin_title,
+ );
+
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#title' => t('Machine name'),
+ '#machine_name' => array(
+ 'exists' => 'page_manager_page_load',
+ 'source' => array('admin_title'),
+ ),
+ '#description' => t('The machine readable name of this page. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
+ '#default_value' => $page->name,
+ );
+
+ if (isset($page->pid) || empty($form_state['creating'])) {
+ $form['name']['#disabled'] = TRUE;
+ $form['name']['#value'] = $page->name;
+ }
+
+ $form['admin_description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Administrative description'),
+ '#description' => t('A description of what this page is, does or is for, for administrative use.'),
+ '#default_value' => $page->admin_description,
+ );
+
+ // path
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form.'),
+ '#default_value' => $page->path,
+ '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+ );
+
+ $frontpage = variable_get('site_frontpage', 'node');
+
+ $path = array();
+ if ($page->path) {
+ foreach (explode('/', $page->path) as $bit) {
+ if ($bit[0] != '!') {
+ $path[] = $bit;
+ }
+ }
+ }
+
+ $path = implode('/', $path);
+
+ if (empty($path) || $path != $frontpage) {
+ $form['frontpage'] = array(
+ '#type' => 'checkbox',
+ '#default_value' => !empty($page->make_frontpage),
+ '#title' => t('Make this your site home page.'),
+ '#description' => t('To set this panel as your home page you must create a unique path name with no % placeholders in the path. The site home page is currently set to %homepage on the !siteinfo configuration form.', array('!siteinfo' => l(t('Site Information'), 'admin/config/system/site-information'), '%homepage' => '/' . $frontpage)),
+ );
+ $form['admin_paths'] = array(
+ '#type' => 'checkbox',
+ '#default_value' => !empty($page->conf['admin_paths']),
+ '#title' => t('Use this page in an admin overlay.'),
+ '#description' => t('Admin overlays are used in many places in Drupal 7 and administrative custom pages should probably utilize this feature.'),
+ );
+ }
+ else if ($path == $frontpage) {
+ $form['frontpage_markup'] = array(
+ '#value' => '<b>' . t('This page is currently set to be your site home page. This can be modified on the !siteinfo configuration form.', array('!siteinfo' => l(t('Site Information'), 'admin/settings/site-information'))) . '</b>',
+ );
+
+ $form['frontpage'] = array(
+ '#type' => 'value',
+ '#value' => TRUE,
+ );
+ }
+
+ if (!isset($page->pid) && !empty($form_state['creating'])) {
+ $features['default'] = array(
+ 'access' => t('Access control'),
+ 'menu' => t('Visible menu item'),
+ );
+
+ module_load_include('inc', 'page_manager', 'page_manager.admin');
+ $form = page_manager_handler_add_form($form, $form_state, $features);
+ }
+
+ return $form;
+}
+
+function page_manager_page_form_basic_validate_filter($value) {
+ return $value === -1;
+}
+
+/**
+ * Validate the basic form.
+ */
+function page_manager_page_form_basic_validate(&$form, &$form_state) {
+ // Ensure path is unused by other pages.
+ $page = $form_state['page']->subtask['subtask'];
+ $name = !empty($form_state['values']['name']) ? $form_state['values']['name'] : $page->name;
+ if (empty($name)) {
+ form_error($form['name'], t('Name is required.'));
+ }
+
+ // If this is new, make sure the name is unique:
+ if (empty($page->name)) {
+ $test = page_manager_page_load($name);
+ if ($test) {
+ form_error($form['name'], t('That name is used by another page: @page', array('@page' => $test->admin_title)));
+ }
+
+ // Ensure name fits the rules:
+ if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
+ form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
+ }
+ }
+
+ $pages = page_manager_page_load_all();
+ foreach ($pages as $test) {
+ if ($test->name != $name && $test->path == $form_state['values']['path'] && empty($test->disabled)) {
+ form_error($form['path'], t('That path is used by another page: @page', array('@page' => $test->admin_title)));
+ }
+ }
+
+ // Ensure path is unused by things NOT pages. We do the double check because
+ // we're checking against our page callback.
+ $path = array();
+ if (empty($form_state['values']['path'])) {
+ form_error($form['path'], t('Path is required.'));
+ // stop processing here if there is no path.
+ return;
+ }
+
+ $found = FALSE;
+ $error = FALSE;
+ foreach (explode('/', $form_state['values']['path']) as $position => $bit) {
+ if (!isset($bit) || $bit === '') {
+ continue;
+ }
+
+ if ($bit == '%' || $bit == '!') {
+ form_error($form['path'], t('You cannot have an unnamed placeholder (% or ! by itself). Please name your placeholder by adding a short piece of descriptive text to the % or !, such as %user or %node.'));
+ }
+
+ if ($bit[0] == '%') {
+ if ($found) {
+ form_error($form['path'], t('You cannot have a dynamic path element after an optional path element.'));
+ }
+
+ if ($position == 0) {
+ form_error($form['path'], t('The first element in a path may not be dynamic.'));
+ }
+
+ $path[] = '%';
+ }
+ else if ($bit[0] == '!') {
+ $found = TRUE;
+ }
+ else {
+ if ($found) {
+ form_error($form['path'], t('You cannot have a static path element after an optional path element.'));
+ }
+ $path[] = $bit;
+ }
+ }
+
+ // Check to see if something that isn't a page manager page is using the path.
+ $path = implode('/', $path);
+ $result = db_query('SELECT * FROM {menu_router} WHERE path = :path', array(':path' => $path));
+ foreach ($result as $router) {
+ if ($router->page_callback != 'page_manager_page_execute') {
+ form_error($form['path'], t('That path is already in use. This system cannot override existing paths.'));
+ }
+ }
+
+ // Ensure the path is not already an alias to something else.
+ if (strpos($path, '%') === FALSE) {
+ $alias = db_query('SELECT alias, source FROM {url_alias} WHERE alias = :path', array(':path' => $path))->fetchObject();
+ if ($alias) {
+ form_error($form['path'], t('That path is currently assigned to be an alias for @alias. This system cannot override existing aliases.', array('@alias' => $alias->source)));
+ }
+ }
+ else {
+ if (!empty($form_state['values']['frontpage'])) {
+ form_error($form['path'], t('You cannot make this page your site home page if it uses % placeholders.'));
+ }
+ }
+
+ // Ensure path is properly formed.
+ $args = page_manager_page_get_named_arguments($form_state['values']['path']);
+ if ($invalid_args = array_filter($args, 'page_manager_page_form_basic_validate_filter')) {
+ foreach ($invalid_args as $arg => $position) {
+ form_error($form['path'], t('Duplicated argument %arg', array('%arg' => $arg)));
+ }
+ }
+
+ if (isset($args['%'])) {
+ form_error($form['path'], t('Invalid arg <em>%</em>. All arguments must be named with keywords.'));
+ }
+
+ $form_state['arguments'] = $args;
+}
+
+/**
+ * Store the values from the basic settings form.
+ */
+function page_manager_page_form_basic_submit(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $cache = &$form_state['page'];
+
+ // If this is a new thing, then we have to do a bunch of setup to create
+ // the cache record with the right ID and some basic data that we could
+ // not know until we asked the user some questions.
+ if (!isset($page->pid) && !empty($form_state['creating'])) {
+ // Update the data with our new name.
+ $page->name = $form_state['values']['name'];
+ $form_state['page']->task_name = page_manager_make_task_name($form_state['task_id'], $page->name);
+ $cache->handler = $form_state['values']['handler'];
+ $cache->subtask_id = $page->name;
+ $plugin = page_manager_get_task_handler($cache->handler);
+
+ // If they created and went back, there might be old, dead handlers
+ // that are not going to be added.
+ //
+ // Remove them:
+ $cache->handlers = array();
+ $cache->handler_info = array();
+
+ // Create a new handler.
+ $handler = page_manager_new_task_handler($plugin);
+ $title = !empty($form_state['values']['title']) ? $form_state['values']['title'] : $plugin['title'];
+ page_manager_handler_add_to_page($cache, $handler, $title);
+
+ // Figure out which forms to present them with
+ $cache->forms = array();
+ $cache->forms[] = 'basic'; // This one is always there.
+ if (!empty($form_state['arguments'])) {
+ $cache->forms[] = 'argument';
+ }
+
+ $features = $form_state['values']['features'];
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features['default'])));
+ if (isset($features[$form_state['values']['handler']])) {
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features[$form_state['values']['handler']])));
+ }
+
+ if (isset($plugin['required forms'])) {
+ $cache->forms = array_merge($cache->forms, array_keys($plugin['required forms']));
+ }
+ }
+
+ $page->admin_title = $form_state['values']['admin_title'];
+ $cache->subtask['admin title'] = check_plain($form_state['values']['admin_title']);
+
+ $page->admin_description = $form_state['values']['admin_description'];
+ $cache->subtask['admin description'] = filter_xss_admin($form_state['values']['admin_description']);
+
+ if ($page->path != $form_state['values']['path']) {
+ $page->path = $form_state['values']['path'];
+ page_manager_page_recalculate_arguments($page);
+ $cache->path_changed = TRUE;
+ }
+
+ $page->make_frontpage = !empty($form_state['values']['frontpage']);
+ $page->conf['admin_paths'] = !empty($form_state['values']['admin_paths']);
+}
+
+/**
+ * Form to handle menu item controls.
+ */
+function page_manager_page_form_menu($form, &$form_state) {
+ ctools_include('dependent');
+ $form['menu'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+
+ $menu = $form_state['page']->subtask['subtask']->menu;
+ if (empty($menu)) {
+ $menu = array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ 'parent' => array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ ),
+ );
+ }
+
+ $form['menu']['type'] = array(
+ '#title' => t('Type'),
+ '#type' => 'radios',
+ '#options' => array(
+ 'none' => t('No menu entry'),
+ 'normal' => t('Normal menu entry'),
+ 'tab' => t('Menu tab'),
+ 'default tab' => t('Default menu tab'),
+ 'action' => t('Local action'),
+ ),
+ '#default_value' => $menu['type'],
+ );
+
+ $form['menu']['title'] = array(
+ '#title' => t('Title'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['title'],
+ '#description' => t('If set to normal or tab, enter the text to use for the menu item.'),
+ '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab', 'action')),
+ );
+
+ list($major, $minor) = explode('.', VERSION, 2);
+
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['name'] = array(
+ '#title' => t('Menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $menu['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#dependency' => array('radio:menu[type]' => array('normal')),
+ );
+ }
+ else {
+ $form['menu']['name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['name'],
+ );
+ $form['menu']['markup'] = array(
+ '#value' => t('Menu selection requires the activation of menu module.'),
+ );
+ }
+ $form['menu']['weight'] = array(
+ '#title' => t('Weight'),
+ '#type' => 'textfield',
+ '#default_value' => isset($menu['weight']) ? $menu['weight'] : 0,
+ '#description' => t('The lower the weight the higher/further left it will appear.'),
+ '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab', 'action')),
+ );
+
+ $form['menu']['parent']['type'] = array(
+ '#prefix' => '<div id="edit-menu-parent-type-wrapper">',
+ '#suffix' => '</div>',
+ '#title' => t('Parent menu item'),
+ '#type' => 'radios',
+ '#options' => array('none' => t('No menu entry'), 'normal' => t('Normal menu item'), 'tab' => t('Menu tab')),
+ '#default_value' => $menu['parent']['type'],
+ '#description' => t('When providing a menu item as a default tab, Drupal needs to know what the parent menu item of that tab will be. Sometimes the parent will already exist, but other times you will need to have one created. The path of a parent item will always be the same path with the last part left off. i.e, if the path to this view is <em>foo/bar/baz</em>, the parent path would be <em>foo/bar</em>.'),
+ '#dependency' => array('radio:menu[type]' => array('default tab')),
+ );
+ $form['menu']['parent']['title'] = array(
+ '#title' => t('Parent item title'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['parent']['title'],
+ '#description' => t('If creating a parent menu item, enter the title of the item.'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal', 'tab')),
+ '#dependency_count' => 2,
+ );
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['parent']['name'] = array(
+ '#title' => t('Parent item menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $menu['parent']['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal')),
+ '#dependency_count' => 2,
+ );
+ }
+ else {
+ $form['menu']['parent']['name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['parent']['name'],
+ );
+ }
+ $form['menu']['parent']['weight'] = array(
+ '#title' => t('Parent weight'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['parent']['weight'],
+ '#size' => 5,
+ '#description' => t('Enter the weight of the parent item. The lower the number, the more to the left it will be.'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('tab', 'normal')),
+ '#dependency_count' => 2,
+ );
+
+ return $form;
+}
+
+/**
+ * Validate handler for the menu form for add/edit page task.
+ */
+function page_manager_page_form_menu_validate(&$form, &$form_state) {
+ // If setting a 'normal' menu entry, make sure that any placeholders
+ // support the to_arg stuff.
+
+ if ($form_state['values']['menu']['type'] == 'normal') {
+ $page = $form_state['page']->subtask['subtask'];
+
+ foreach (explode('/', $page->path) as $bit) {
+ if (!isset($bit) || $bit === '') {
+ continue;
+ }
+
+ if ($bit[0] == '%') {
+ // Chop off that %.
+ $name = substr($bit, 1);
+
+ // Check to see if the argument plugin allows to arg:
+ if (!empty($page->arguments[$name])) {
+ ctools_include('context');
+ $plugin = ctools_get_argument($page->arguments[$name]['name']);
+ if (!empty($plugin['path placeholder to_arg'])) {
+ continue;
+ }
+ }
+
+ form_error($form['menu']['type'], t('Paths with non optional placeholders cannot be used as normal menu items unless the selected argument handler provides a default argument to use for the menu item.'));
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * Submit handler for the menu form for add/edit page task.
+ */
+function page_manager_page_form_menu_submit(&$form, &$form_state) {
+ $form_state['page']->subtask['subtask']->menu = $form_state['values']['menu'];
+ $form_state['page']->path_changed = TRUE;
+}
+
+/**
+ * Form to handle menu item controls.
+ */
+function page_manager_page_form_access($form, &$form_state) {
+ ctools_include('context');
+ $form_state['module'] = 'page_manager_page';
+ $form_state['callback argument'] = $form_state['page']->task_name;
+ $form_state['access'] = $form_state['page']->subtask['subtask']->access;
+ $form_state['no buttons'] = TRUE;
+ $form_state['contexts'] = array();
+
+ // Load contexts based on argument data:
+ if ($arguments = _page_manager_page_get_arguments($form_state['page']->subtask['subtask'])) {
+ $form_state['contexts'] = ctools_context_get_placeholders_from_argument($arguments);
+ }
+
+ ctools_include('context-access-admin');
+ $form = ctools_access_admin_form($form, $form_state);
+
+ return $form;
+}
+
+/**
+ * Submit handler to deal with access control changes.
+ */
+function page_manager_page_form_access_submit(&$form, &$form_state) {
+ $form_state['page']->subtask['subtask']->access['logic'] = $form_state['values']['logic'];
+ $form_state['page']->path_changed = TRUE;
+}
+
+/**
+ * Form to handle assigning argument handlers to named arguments.
+ */
+function page_manager_page_form_argument($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $path = $page->path;
+
+ $arguments = page_manager_page_get_named_arguments($path);
+
+ $form['table'] = array(
+ '#theme' => 'page_manager_page_form_argument_table',
+ '#page-manager-path' => $path,
+ 'argument' => array(),
+ );
+
+ $task_name = $form_state['page']->task_name;
+ foreach ($arguments as $keyword => $position) {
+ $conf = array();
+
+ if (isset($page->temporary_arguments[$keyword]) && !empty($form_state['allow temp'])) {
+ $conf = $page->temporary_arguments[$keyword];
+ }
+ else if (isset($page->arguments[$keyword])) {
+ $conf = $page->arguments[$keyword];
+ }
+
+ $context = t('No context assigned');
+
+ $plugin = array();
+ if ($conf && isset($conf['name'])) {
+ ctools_include('context');
+ $plugin = ctools_get_argument($conf['name']);
+
+ if (isset($plugin['title'])) {
+ $context = $plugin['title'];
+ }
+ }
+
+ $form['table']['argument'][$keyword]['#keyword'] = $keyword;
+ $form['table']['argument'][$keyword]['#position'] = $position;
+ $form['table']['argument'][$keyword]['#context'] = $context;
+
+ // The URL for this ajax button
+ $form['table']['argument'][$keyword]['change-url'] = array(
+ '#attributes' => array('class' => array("page-manager-context-$keyword-change-url")),
+ '#type' => 'hidden',
+ '#value' => url("admin/structure/pages/argument/change/$task_name/$keyword", array('absolute' => TRUE)),
+ );
+ $form['table']['argument'][$keyword]['change'] = array(
+ '#type' => 'submit',
+ '#value' => t('Change'),
+ '#attributes' => array('class' => array('ctools-use-modal')),
+ '#id' => "page-manager-context-$keyword-change",
+ );
+
+ $form['table']['argument'][$keyword]['settings'] = array();
+
+ // Only show the button if this has a settings form available:
+ if (!empty($plugin)) {
+ // The URL for this ajax button
+ $form['table']['argument'][$keyword]['settings-url'] = array(
+ '#attributes' => array('class' => array("page-manager-context-$keyword-settings-url")),
+ '#type' => 'hidden',
+ '#value' => url("admin/structure/pages/argument/settings/$task_name/$keyword", array('absolute' => TRUE)),
+ );
+ $form['table']['argument'][$keyword]['settings'] = array(
+ '#type' => 'submit',
+ '#value' => t('Settings'),
+ '#attributes' => array('class' => array('ctools-use-modal')),
+ '#id' => "page-manager-context-$keyword-settings",
+ );
+ }
+ }
+
+ return $form;
+}
+
+/**
+ * Theme the table for this form.
+ */
+function theme_page_manager_page_form_argument_table($vars) {
+ $form = $vars['form'];
+ $header = array(
+ array('data' => t('Argument'), 'class' => array('page-manager-argument')),
+ array('data' => t('Position in path'), 'class' => array('page-manager-position')),
+ array('data' => t('Context assigned'), 'class' => array('page-manager-context')),
+ array('data' => t('Operations'), 'class' => array('page-manager-operations')),
+ );
+
+ $rows = array();
+
+ ctools_include('modal');
+ ctools_modal_add_js();
+ foreach (element_children($form['argument']) as $key) {
+ $row = array();
+ $row[] = '%' . check_plain($form['argument'][$key]['#keyword']);
+ $row[] = check_plain($form['argument'][$key]['#position']);
+ $row[] = $form['argument'][$key]['#context'] . ' &nbsp; ' . drupal_render($form['argument'][$key]['change']);;
+ $row[] = drupal_render($form['argument'][$key]['settings']) . drupal_render($form['argument'][$key]);
+
+ $rows[] = array('data' => $row);
+ }
+
+ if (!$rows) {
+ $rows[] = array(array('data' => t('The path %path has no arguments to configure.', array('%path' => $form['#page-manager-path'])), 'colspan' => 4));
+ }
+
+ $attributes = array(
+ 'id' => 'page-manager-argument-table',
+ );
+
+ $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes));
+ return $output;
+}
+
+/**
+ * Ajax entry point to edit an item
+ */
+function page_manager_page_subtask_argument_ajax($step = NULL, $task_name = NULL, $keyword = NULL) {
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_include('context');
+ ctools_include('wizard');
+
+ if (!$step) {
+ return ctools_ajax_render_error();
+ }
+
+ if (!$cache = page_manager_get_page_cache($task_name)) {
+ return ctools_ajax_render_error(t('Invalid object name.'));
+ }
+
+ $page = &$cache->subtask['subtask'];
+ $path = $page->path;
+ $arguments = page_manager_page_get_named_arguments($path);
+
+ // Load stored object from cache.
+ if (!isset($arguments[$keyword])) {
+ return ctools_ajax_render_error(t('Invalid keyword.'));
+ }
+
+ // Set up wizard info
+ $form_info = array(
+ 'id' => 'page_manager_page_argument',
+ 'path' => "admin/structure/pages/argument/%step/$task_name/$keyword",
+ 'show cancel' => TRUE,
+ 'next callback' => 'page_manager_page_argument_next',
+ 'finish callback' => 'page_manager_page_argument_finish',
+ 'cancel callback' => 'page_manager_page_argument_cancel',
+ 'order' => array(
+ 'change' => t('Change context type'),
+ 'settings' => t('Argument settings'),
+ ),
+ 'forms' => array(
+ 'change' => array(
+ 'title' => t('Change argument'),
+ 'form id' => 'page_manager_page_argument_form_change',
+ ),
+ 'settings' => array(
+ 'title' => t('Argument settings'),
+ 'form id' => 'page_manager_page_argument_form_settings',
+ ),
+ ),
+ );
+
+ $form_state = array(
+ 'page' => $cache,
+ 'keyword' => $keyword,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ 'modal return' => TRUE,
+ 'commands' => array(),
+ );
+
+ $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
+ if (!empty($form_state['cancel'])) {
+ $commands = array(ctools_modal_command_dismiss());
+ }
+ else if (!empty($form_state['complete'])) {
+ if (isset($page->temporary_arguments[$keyword])) {
+ $page->arguments[$keyword] = $page->temporary_arguments[$keyword];
+ }
+
+ if (isset($page->temporary_arguments)) {
+ unset($page->temporary_arguments);
+ }
+
+ // Update the cache with changes.
+ page_manager_set_page_cache($cache);
+
+ // Rerender the table so we can ajax it back in.
+ // Go directly to the form and retrieve it using a blank form and
+ // a clone of our current form state. This is an abbreviated
+ // drupal_get_form that is halted prior to render and is never
+ // fully processed, but is guaranteed to produce the same form we
+ // started with so we don't have to do crazy stuff to rerender
+ // just part of it.
+
+ // @todo should there be a tool to do this?
+
+ $clone_state = $form_state;
+ $clone_state['allow temp'] = TRUE;
+ $form = drupal_build_form('page_manager_page_form_argument', $form_state);
+
+ // Render just the table portion.
+ $output = drupal_render($form['table']);
+ $commands = array(
+ ajax_command_replace('#page-manager-argument-table', $output),
+ ctools_modal_command_dismiss(),
+ );
+ }
+ else {
+ $commands = ctools_modal_form_render($form_state, $output);
+ }
+ print ajax_render($commands);
+ ajax_footer();
+ exit;
+}
+
+/**
+ * Callback generated when the add page process is finished.
+ */
+function page_manager_page_argument_finish(&$form_state) {
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_page_argument_next(&$form_state) {
+ // Update the cache with changes.
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * We might have some temporary data lying around. We must remove it.
+ */
+function page_manager_page_argument_cancel(&$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ if (isset($page->temporary_arguments)) {
+ unset($page->temporary_arguments);
+ // Update the cache with changes.
+ page_manager_set_page_cache($page);
+ }
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_argument_form_change($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+
+ ctools_include('context');
+ $plugins = ctools_get_arguments();
+
+ $options = array();
+ foreach ($plugins as $id => $plugin) {
+ if (empty($plugin['no ui'])) {
+ $options[$id] = $plugin['title'];
+ }
+ }
+
+ asort($options);
+
+ $options = array('' => t('No context selected')) + $options;
+
+ $argument = '';
+ if (isset($page->arguments[$keyword]) && isset($page->arguments[$keyword]['name'])) {
+ $argument = $page->arguments[$keyword]['name'];
+ }
+
+ $form['argument'] = array(
+ '#type' => 'radios',
+ '#options' => $options,
+ '#default_value' => $argument,
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler to change an argument.
+ */
+function page_manager_page_argument_form_change_submit(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+ $argument = $form_state['values']['argument'];
+
+ // If the argument is not changing, we do not need to do anything.
+ if (isset($page->arguments[$keyword]['name']) && $page->arguments[$keyword]['name'] == $argument) {
+ // Set the task to cancel since no change means do nothing:
+ $form_state['clicked_button']['#wizard type'] = 'cancel';
+ return;
+ }
+
+ ctools_include('context');
+
+ // If switching to the no context, just wipe out the old data.
+ if (empty($argument)) {
+ $form_state['clicked_button']['#wizard type'] = 'finish';
+ $page->temporary_arguments[$keyword] = array(
+ 'settings' => array(),
+ 'identifier' => t('No context'),
+ );
+ return;
+ }
+
+ $plugin = ctools_get_argument($argument);
+
+ // Acquire defaults.
+ $settings = array();
+
+ if (isset($plugin['default'])) {
+ if (is_array($plugin['default'])) {
+ $settings = $plugin['default'];
+ }
+ else if (function_exists($plugin['default'])) {
+ $settings = $plugin['default']();
+ }
+ }
+
+ $id = ctools_context_next_id($page->arguments, $argument);
+ $title = isset($plugin['title']) ? $plugin['title'] : t('No context');
+
+ // Set the new argument in a temporary location.
+ $page->temporary_arguments[$keyword] = array(
+ 'id' => $id,
+ 'identifier' => $title . ($id > 1 ? ' ' . $id : ''),
+ 'name' => $argument,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_argument_form_settings($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+
+ if (isset($page->temporary_arguments[$keyword])) {
+ $conf = $page->temporary_arguments[$keyword];
+ }
+ else if (isset($page->arguments[$keyword])) {
+ $conf = $page->temporary_arguments[$keyword] = $page->arguments[$keyword];
+ }
+
+ if (!isset($conf)) {
+ // This should be impossible and thus never seen.
+ $form['error'] = array('#value' => t('Error: missing argument.'));
+ return;
+ }
+
+ ctools_include('context');
+ $plugin = ctools_get_argument($conf['name']);
+
+ $form['settings'] = array(
+ '#tree' => TRUE,
+ );
+
+ $form['identifier'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Context identifier'),
+ '#description' => t('This is the title of the context used to identify it later in the administrative process. This will never be shown to a user.'),
+ '#default_value' => $conf['identifier'],
+ );
+
+ if (!$plugin) {
+ // This should be impossible and thus never seen.
+ $form['error'] = array('#value' => t('Error: missing or invalid argument plugin %argument.', array('%argument', $argument)));
+ return;
+ }
+
+ if ($function = ctools_plugin_get_function($plugin, 'settings form')) {
+ $function($form, $form_state, $conf['settings']);
+ }
+
+ $form_state['plugin'] = $plugin;
+ return $form;
+}
+
+/**
+ * Validate handler for argument settings.
+ */
+function page_manager_page_argument_form_settings_validate(&$form, &$form_state) {
+ if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form validate')) {
+ $function($form, $form_state);
+ }
+}
+
+/**
+ * Submit handler for argument settings.
+ */
+function page_manager_page_argument_form_settings_submit(&$form, &$form_state) {
+ if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form submit')) {
+ $function($form, $form_state);
+ }
+
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+ // Copy the form to our temporary location which will get moved again when
+ // finished. Yes, finished is always next but finish can happen from other
+ // locations so we funnel through that path rather than duplicate.
+ $page->temporary_arguments[$keyword]['identifier'] = $form_state['values']['identifier'];
+ if (isset($form_state['values']['settings'])) {
+ $page->temporary_arguments[$keyword]['settings'] = $form_state['values']['settings'];
+ }
+ else {
+ $page->temporary_arguments[$keyword]['settings'] = array();
+ }
+}
+
+/**
+ * Import a task handler from cut & paste
+ */
+function page_manager_page_import_subtask($form, &$form_state, $task_name) {
+ $form_state['task'] = page_manager_get_task($task_name);
+
+ drupal_set_title(t('Import page'));
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Page name'),
+ '#description' => t('Enter the name to use for this page if it is different from the source page. Leave blank to use the original name of the page.'),
+ );
+
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('Enter the path to use for this page if it is different from the source page. Leave blank to use the original path of the page.'),
+ );
+
+ $form['overwrite'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow overwrite of an existing page'),
+ '#description' => t('If the name you selected already exists in the database, this page will be allowed to overwrite the existing page.'),
+ );
+
+ $form['object'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Paste page code here'),
+ '#rows' => 15,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Import'),
+ );
+ return $form;
+}
+
+/**
+ * Ensure we got a valid page.
+ */
+function page_manager_page_import_subtask_validate(&$form, &$form_state) {
+ ob_start();
+ eval($form_state['values']['object']);
+ ob_end_clean();
+
+ if (!isset($page) || !is_object($page)) {
+ $errors = ob_get_contents();
+ if (empty($errors)) {
+ $errors = t('No handler found.');
+ }
+ form_error($form['object'], t('Unable to get a page from the import. Errors reported: @errors', array('@errors' => $errors)));
+ }
+
+ if (empty($form_state['values']['name'])) {
+ $form_state['values']['name'] = $page->name;
+ }
+
+ $task_name = page_manager_make_task_name('page', $form_state['values']['name']);
+ $form_state['cache'] = page_manager_get_page_cache($task_name);
+
+ if ($form_state['cache'] && $form_state['cache']->locked) {
+ form_error($form['name'], t('That page name is in use and locked by another user. You must <a href="!break">break the lock</a> on that page before proceeding, or choose a different name.', array('!break' => url(page_manager_edit_url($task_name, array('actions', 'break-lock'))))));
+ return;
+ }
+
+ if (empty($form_state['values']['path'])) {
+ $form_state['values']['path'] = $page->path;
+ }
+
+ if (empty($form_state['values']['overwrite'])) {
+ $page->name = NULL;
+ }
+
+ $form_state['page'] = new stdClass();
+ $form_state['page']->subtask['subtask'] = $page;
+ page_manager_page_form_basic_validate($form, $form_state);
+}
+
+/**
+ * Submit the import page to create the new page and redirect.
+ */
+function page_manager_page_import_subtask_submit($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $page->name = $form_state['values']['name'];
+ $page->path = $form_state['values']['path'];
+
+ $task_name = page_manager_make_task_name('page', $page->name);
+ $cache = page_manager_get_page_cache($task_name);
+ if (!$cache) {
+ $cache = new stdClass();
+ }
+
+ page_manager_page_new_page_cache($page, $cache);
+ page_manager_set_page_cache($cache);
+
+ $form_state['redirect'] = page_manager_edit_url($task_name);
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_page_form_export($form, &$form_state) {
+ $page = $form_state['page']->subtask['subtask'];
+
+ $export = page_manager_page_export($page, $form_state['page']->handlers);
+
+ $lines = substr_count($export, "\n");
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#default_value' => $export,
+ '#rows' => $lines,
+ );
+
+ unset($form['buttons']);
+ return $form;
+}
+
+/**
+ * Entry point to clone a page.
+ */
+function page_manager_page_form_clone($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ // This provides its own button because it does something totally different.
+ unset($form['buttons']);
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
+ '#default_value' => $page->admin_title,
+ );
+
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#title' => t('Page name'),
+ '#machine_name' => array(
+ 'exists' => 'page_manager_page_load',
+ 'source' => array('admin_title'),
+ ),
+ '#description' => t('Enter the name to the new page It must be unique and contain only alphanumeric characters and underscores.'),
+ );
+
+ // path
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form. You cannot use the same path as the original page.'),
+ '#default_value' => $page->path,
+ );
+
+ $form['handlers'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Clone variants'),
+ '#description' => t('If checked all variants associated with the page will be cloned as well. If not checked the page will be cloned without variants.'),
+ '#default_value' => TRUE,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Clone'),
+ );
+
+ return $form;
+}
+
+/**
+ * Validate clone page form.
+ */
+function page_manager_page_form_clone_validate(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ $page->old_name = $page->name;
+ $page->name = NULL;
+ page_manager_page_form_basic_validate($form, $form_state);
+}
+
+/**
+ * submit clone page form.
+ *
+ * Load the page, change the name(s) to protect the innocent, and if
+ * requested, load all the task handlers so that they get saved properly too.
+ */
+function page_manager_page_form_clone_submit(&$form, &$form_state) {
+ $original = $form_state['page']->subtask['subtask'];
+
+ $original->name = $form_state['values']['name'];
+ $original->admin_title = $form_state['values']['admin_title'];
+ $original->path = $form_state['values']['path'];
+
+ $handlers = !empty($form_state['values']['handlers']) ? $form_state['page']->handlers : FALSE;
+ // Export the handler, which is a fantastic way to clean database IDs out of it.
+ $export = page_manager_page_export($original, $handlers);
+ ob_start();
+ eval($export);
+ ob_end_clean();
+
+ $task_name = page_manager_make_task_name('page', $page->name);
+ $cache = new stdClass();
+
+ page_manager_page_new_page_cache($page, $cache);
+ page_manager_set_page_cache($cache);
+
+ $form_state['redirect'] = page_manager_edit_url($task_name);
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_page_form_delete($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ if ($page->type == t('Overridden')) {
+ $text = t('Reverting the page will delete the page that is in the database, reverting it to the original default page. Any changes you have made will be lost and cannot be recovered.');
+ }
+ else {
+ $text = t('Are you sure you want to delete this page? Deleting a page cannot be undone.');
+ }
+ $form['markup'] = array(
+ '#value' => '<p>' . $text . '</p>',
+ );
+
+ if (empty($form_state['page']->locked)) {
+ unset($form['buttons']);
+ $form['delete'] = array(
+ '#type' => 'submit',
+ '#value' => $page->type == t('Overridden') ? t('Revert') : t('Delete'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Submit handler to delete a view.
+ */
+function page_manager_page_form_delete_submit(&$form, &$form_state) {
+ $page = $form_state['page']->subtask['subtask'];
+ page_manager_page_delete($page);
+ if ($page->type != t('Overridden')) {
+ $form_state['redirect'] = 'admin/structure/pages';
+ drupal_set_message(t('The page has been deleted.'));
+ }
+ else {
+ $form_state['redirect'] = page_manager_edit_url($form_state['page']->task_name, array('summary'));
+ drupal_set_message(t('The page has been reverted.'));
+ }
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/page.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/page.inc
new file mode 100644
index 000000000..6a8545d1a
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/page.inc
@@ -0,0 +1,787 @@
+<?php
+
+/**
+ * @file
+ * Handle the 'page' task, which creates pages with arbitrary tasks and lets
+ * handlers decide how they will be rendered.
+ *
+ * This creates subtasks and stores them in the page_manager_pages table. These
+ * are exportable objects, too.
+ *
+ * The render callback for this task type has $handler, $page, $contexts as
+ * parameters.
+ */
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_page_page_manager_tasks() {
+ return array(
+ 'title' => t('Custom pages'),
+ 'description' => t('Administrator created pages that have a URL path, access control and entries in the Drupal menu system.'),
+ 'non-exportable' => TRUE,
+ 'subtasks' => TRUE,
+ 'subtask callback' => 'page_manager_page_subtask',
+ 'subtasks callback' => 'page_manager_page_subtasks',
+ 'save subtask callback' => 'page_manager_page_save_subtask',
+ 'access callback' => 'page_manager_page_access_check',
+ 'hook menu' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ 'function' => 'page_manager_page_menu',
+ ),
+ 'hook theme' => 'page_manager_page_theme',
+ // page only items
+ 'task type' => 'page',
+ 'page operations' => array(
+ array(
+ 'title' => ' &raquo; ' . t('Create a new page'),
+ 'href' => 'admin/structure/pages/add',
+ 'html' => TRUE,
+ ),
+ ),
+ 'columns' => array(
+ 'storage' => array(
+ 'label' => t('Storage'),
+ 'class' => 'page-manager-page-storage',
+ ),
+ ),
+ 'page type' => 'custom',
+
+ // context only items
+ 'handler type' => 'context',
+ 'get arguments' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ 'function' => 'page_manager_page_get_arguments',
+ ),
+ 'get context placeholders' => 'page_manager_page_get_contexts',
+ 'access restrictions' => 'page_manager_page_access_restrictions',
+ 'uses handlers' => TRUE,
+ );
+}
+
+/**
+ * Task callback to get all subtasks.
+ *
+ * Return a list of all subtasks.
+ */
+function page_manager_page_subtasks($task) {
+ $pages = page_manager_page_load_all($task['name']);
+ $return = array();
+ foreach ($pages as $name => $page) {
+ $return[$name] = page_manager_page_build_subtask($task, $page);
+ }
+
+ return $return;
+}
+
+/**
+ * Callback to return a single subtask.
+ */
+function page_manager_page_subtask($task, $subtask_id) {
+ $page = page_manager_page_load($subtask_id);
+ if ($page) {
+ return page_manager_page_build_subtask($task, $page);
+ }
+}
+
+/**
+ * Call back from the administrative system to save a page.
+ *
+ * We get the $subtask as created by page_manager_page_build_subtask.
+ */
+function page_manager_page_save_subtask($subtask) {
+ $page = &$subtask['subtask'];
+
+ // Ensure $page->arguments contains only real arguments:
+ $arguments = page_manager_page_get_named_arguments($page->path);
+ $args = array();
+ foreach ($arguments as $keyword => $position) {
+ if (isset($page->arguments[$keyword])) {
+ $args[$keyword] = $page->arguments[$keyword];
+ }
+ else {
+ $args[$keyword] = array(
+ 'id' => '',
+ 'identifier' => '',
+ 'argument' => '',
+ 'settings' => array(),
+ );
+ }
+ }
+ page_manager_page_recalculate_arguments($page);
+ // Create a real object from the cache
+ page_manager_page_save($page);
+
+ // Check to see if we should make this the site frontpage.
+ if (!empty($page->make_frontpage)) {
+ $path = array();
+ foreach (explode('/', $page->path) as $bit) {
+ if ($bit[0] != '!') {
+ $path[] = $bit;
+ }
+ }
+
+ $path = implode('/', $path);
+ $front = variable_get('site_frontpage', 'node');
+ if ($path != $front) {
+ variable_set('site_frontpage', $path);
+ }
+ }
+}
+
+/**
+ * Build a subtask array for a given page.
+ */
+function page_manager_page_build_subtask($task, $page) {
+ $operations = array();
+ $operations['settings'] = array(
+ 'type' => 'group',
+ 'class' => array('operations-settings'),
+ 'title' => t('Settings'),
+ 'children' => array(),
+ );
+
+ $settings = &$operations['settings']['children'];
+
+ $settings['basic'] = array(
+ 'title' => t('Basic'),
+ 'description' => t('Edit name, path and other basic settings for the page.'),
+ 'form' => 'page_manager_page_form_basic',
+ );
+
+ $arguments = page_manager_page_get_named_arguments($page->path);
+ if ($arguments) {
+ $settings['argument'] = array(
+ 'title' => t('Arguments'),
+ 'description' => t('Set up contexts for the arguments on this page.'),
+ 'form' => 'page_manager_page_form_argument',
+ );
+ }
+
+ $settings['access'] = array(
+ 'title' => t('Access'),
+ 'description' => t('Control what users can access this page.'),
+ 'admin description' => t('Access rules are used to test if the page is accessible and any menu items associated with it are visible.'),
+ 'form' => 'page_manager_page_form_access',
+ );
+
+ $settings['menu'] = array(
+ 'title' => t('Menu'),
+ 'description' => t('Provide this page a visible menu or a menu tab.'),
+ 'form' => 'page_manager_page_form_menu',
+ );
+
+ $operations['actions']['children']['clone'] = array(
+ 'title' => t('Clone'),
+ 'description' => t('Make a copy of this page'),
+ 'form' => 'page_manager_page_form_clone',
+ );
+ $operations['actions']['children']['export'] = array(
+ 'title' => t('Export'),
+ 'description' => t('Export this page as code that can be imported or embedded into a module.'),
+ 'form' => 'page_manager_page_form_export',
+ );
+ if ($page->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
+ $operations['actions']['children']['delete'] = array(
+ 'title' => t('Revert'),
+ 'description' => t('Remove all changes to this page and revert to the version in code.'),
+ 'form' => 'page_manager_page_form_delete',
+ );
+ }
+ else if ($page->export_type != EXPORT_IN_CODE) {
+ $operations['actions']['children']['delete'] = array(
+ 'title' => t('Delete'),
+ 'description' => t('Remove this page from your system completely.'),
+ 'form' => 'page_manager_page_form_delete',
+ );
+ }
+
+ $subtask = array(
+ 'name' => $page->name,
+ 'admin title' => check_plain($page->admin_title),
+ 'admin description' => filter_xss_admin($page->admin_description),
+ 'admin summary' => 'page_manager_page_admin_summary',
+ 'admin path' => $page->path,
+ 'admin type' => t('Custom'),
+ 'subtask' => $page,
+ 'operations' => $operations,
+ 'operations include' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ ),
+ 'single task' => empty($page->multiple),
+ 'row class' => empty($page->disabled) ? 'page-manager-enabled' : 'page-manager-disabled',
+ 'storage' => $page->type == t('Default') ? t('In code') : $page->type,
+ 'disabled' => !empty($page->disabled),
+ // This works for both enable AND disable
+ 'enable callback' => 'page_manager_page_enable',
+ );
+
+ // default handlers may appear from a default subtask.
+ if (isset($page->default_handlers)) {
+ $subtask['default handlers'] = $page->default_handlers;
+ }
+ return $subtask;
+}
+
+/**
+ * Delegated implementation of hook_theme().
+ */
+function page_manager_page_theme(&$items, $task) {
+ $base = array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ );
+ $items['page_manager_page_form_argument_table'] = $base + array(
+ 'render element' => 'form',
+ );
+ $items['page_manager_page_lock'] = $base + array(
+ 'variables' => array('lock' => array(), 'task_name' => NULL),
+ );
+ $items['page_manager_page_changed'] = $base + array(
+ 'variables' => array(),
+ );
+}
+
+// --------------------------------------------------------------------------
+// Page execution functions
+
+/**
+ * Execute a page task.
+ *
+ * This is the callback to entries in the Drupal menu system created by the
+ * page task.
+ *
+ * @param $subtask_id
+ * The name of the page task used.
+ * @param ...
+ * A number of context objects as specified by the user when
+ * creating named arguments in the path.
+ */
+function page_manager_page_execute($subtask_id) {
+ $page = page_manager_page_load($subtask_id);
+ $task = page_manager_get_task($page->task);
+ $subtask = page_manager_get_task_subtask($task, $subtask_id);
+
+ // Turn the contexts into a properly keyed array.
+ $contexts = array();
+ $args = array();
+ foreach (func_get_args() as $count => $arg) {
+ if (is_object($arg) && get_class($arg) == 'ctools_context') {
+ $contexts[$arg->id] = $arg;
+ $args[] = $arg->original_argument;
+ }
+ else if ($count) {
+ $args[] = $arg;
+ }
+ }
+
+ $count = 0;
+ $names = page_manager_page_get_named_arguments($page->path);
+ $bits = explode('/', $page->path);
+
+ if ($page->arguments) {
+ foreach ($page->arguments as $name => $argument) {
+ // Optional arguments must be converted to contexts too, if they exist.
+ if ($bits[$names[$name]][0] == '!') {
+ ctools_include('context');
+ $argument['keyword'] = $name;
+ if (isset($args[$count])) {
+ // Hack: use a special argument config variable to learn if we need
+ // to use menu_tail style behavior:
+ if (empty($argument['settings']['use_tail'])) {
+ $value = $args[$count];
+ }
+ else {
+ $value = implode('/', array_slice($args, $count));
+ }
+
+ $context = ctools_context_get_context_from_argument($argument, $value);
+ }
+ else {
+ // make sure there is a placeholder context for missing optional contexts.
+ $context = ctools_context_get_context_from_argument($argument, NULL, TRUE);
+ // Force the title to blank for replacements
+ }
+ if ($context) {
+ $contexts[$context->id] = $context;
+ }
+ }
+ $count++;
+ }
+ }
+
+ if ($function = ctools_plugin_get_function($task, 'page callback')) {
+ return call_user_func_array($function, array($page, $contexts, $args));
+ }
+
+ ctools_include('context-task-handler');
+ $output = ctools_context_handler_render($task, $subtask, $contexts, $args);
+ if ($output === FALSE) {
+ return MENU_NOT_FOUND;
+ }
+
+ return $output;
+}
+
+// --------------------------------------------------------------------------
+// Context type callbacks
+
+/**
+ * Return a list of arguments used by this task.
+ */
+function page_manager_page_get_arguments($task, $subtask) {
+ return _page_manager_page_get_arguments($subtask['subtask']);
+}
+
+function _page_manager_page_get_arguments($page) {
+ $arguments = array();
+ if (!empty($page->arguments)) {
+ foreach ($page->arguments as $keyword => $argument) {
+ if (isset($argument['name'])) {
+ $argument['keyword'] = $keyword;
+ $arguments[$keyword] = $argument;
+ }
+ }
+ }
+ return $arguments;
+}
+
+/**
+ * Get a group of context placeholders for the arguments.
+ */
+function page_manager_page_get_contexts($task, $subtask) {
+ ctools_include('context');
+ return ctools_context_get_placeholders_from_argument(page_manager_page_get_arguments($task, $subtask));
+}
+
+/**
+ * Return a list of arguments used by this task.
+ */
+function page_manager_page_access_restrictions($task, $subtask, $contexts) {
+ $page = $subtask['subtask'];
+ return ctools_access_add_restrictions($page->access, $contexts);
+}
+
+// --------------------------------------------------------------------------
+// Page task database info.
+
+/**
+ * Create a new page with defaults appropriately set from schema.
+ */
+function page_manager_page_new() {
+ ctools_include('export');
+ return ctools_export_new_object('page_manager_pages');
+}
+
+/**
+ * Load a single page subtask.
+ */
+function page_manager_page_load($name) {
+ ctools_include('export');
+ $result = ctools_export_load_object('page_manager_pages', 'names', array($name));
+ if (isset($result[$name])) {
+ return $result[$name];
+ }
+}
+
+/**
+ * Load all page subtasks.
+ */
+function page_manager_page_load_all($task = NULL) {
+ ctools_include('export');
+
+ if (empty($task)) {
+ return ctools_export_load_object('page_manager_pages');
+ }
+ else {
+ return ctools_export_load_object('page_manager_pages', 'conditions', array('task' => $task));
+ }
+}
+
+/**
+ * Write a page subtask to the database.
+ */
+function page_manager_page_save(&$page) {
+ $update = (isset($page->pid)) ? array('pid') : array();
+ $task = page_manager_get_task($page->task);
+
+ if ($function = ctools_plugin_get_function($task, 'save')) {
+ $function($page, $update);
+ }
+ drupal_write_record('page_manager_pages', $page, $update);
+
+ // If this was a default page we may need to write default task
+ // handlers that we provided as well.
+ if (!$update && isset($page->default_handlers)) {
+ $handlers = page_manager_load_task_handlers(page_manager_get_task('page'), $page->name);
+ foreach ($page->default_handlers as $name => $handler) {
+ if (!isset($handlers[$name]) || !($handlers[$name]->export_type & EXPORT_IN_DATABASE)) {
+ // Make sure this is right, as exports can wander a bit.
+ $handler->subtask = $page->name;
+ page_manager_save_task_handler($handler);
+ }
+ }
+ }
+ return $page;
+}
+
+/**
+ * Remove a page subtask.
+ */
+function page_manager_page_delete($page) {
+ $task = page_manager_get_task($page->task);
+ if ($function = ctools_plugin_get_function($task, 'delete')) {
+ $function($page);
+ }
+ if (!empty($task['uses handlers'])) {
+ $handlers = page_manager_load_task_handlers($task, $page->name);
+ foreach ($handlers as $handler) {
+ page_manager_delete_task_handler($handler);
+ }
+ }
+ db_delete('page_manager_pages')
+ ->condition('name', $page->name)
+ ->execute();
+ // Make sure that the cache is reset so that the menu rebuild does not
+ // rebuild this page again.
+ ctools_include('export');
+ ctools_export_load_object_reset('page_manager_pages');
+ menu_rebuild();
+}
+
+/**
+ * Export a page subtask.
+ */
+function page_manager_page_export($page, $with_handlers = FALSE, $indent = '') {
+ $task = page_manager_get_task($page->task);
+ $append = '';
+
+ if ($function = ctools_plugin_get_function($task, 'export')) {
+ $append = $function($page, $indent);
+ }
+
+ ctools_include('export');
+ $output = ctools_export_object('page_manager_pages', $page, $indent);
+ $output .= $append;
+
+ if ($with_handlers) {
+ if (is_array($with_handlers)) {
+ $handlers = $with_handlers;
+ }
+ else {
+ $handlers = page_manager_load_task_handlers(page_manager_get_task('page'), $page->name);
+ }
+ $output .= $indent . '$page->default_handlers = array();' . "\n";
+ foreach ($handlers as $handler) {
+ $output .= page_manager_export_task_handler($handler, $indent);
+ $output .= $indent . '$page->default_handlers[$handler->name] = $handler;' . "\n";
+ }
+ }
+ return $output;
+}
+
+/**
+ * Get a list of named arguments in a page manager path.
+ *
+ * @param $path
+ * A normal Drupal path.
+ *
+ * @return
+ * An array of % marked variable arguments, keyed by the argument's name.
+ * The value will be the position of the argument so that it can easily
+ * be found. Items with a position of -1 have multiple positions.
+ */
+function page_manager_page_get_named_arguments($path) {
+ $arguments = array();
+ $bits = explode('/', $path);
+ foreach ($bits as $position => $bit) {
+ if ($bit && ($bit[0] == '%' || $bit[0] == '!')) {
+ // special handling for duplicate path items and substr to remove the %
+ $arguments[substr($bit, 1)] = isset($arguments[$bit]) ? -1 : $position;
+ }
+ }
+
+ return $arguments;
+}
+
+/**
+ * Load a context from an argument for a given page task.
+ *
+ * Helper function for pm_arg_load(), which is in page_manager.module because
+ * drupal's menu system does not allow loader functions to reside in separate
+ * files.
+ *
+ * @param $value
+ * The incoming argument value.
+ * @param $subtask
+ * The subtask id.
+ * @param $argument
+ * The numeric position of the argument in the path, counting from 0.
+ *
+ * @return
+ * A context item if one is configured, the argument if one is not, or
+ * FALSE if restricted or invalid.
+ */
+function _pm_arg_load($value, $subtask, $argument) {
+ $page = page_manager_page_load($subtask);
+ if (!$page) {
+ return FALSE;
+ }
+
+ $path = explode('/', $page->path);
+ if (empty($path[$argument])) {
+ return FALSE;
+ }
+
+ $keyword = substr($path[$argument], 1);
+ if (empty($page->arguments[$keyword])) {
+ return $value;
+ }
+
+ $page->arguments[$keyword]['keyword'] = $keyword;
+
+ ctools_include('context');
+ $context = ctools_context_get_context_from_argument($page->arguments[$keyword], $value);
+
+ // convert false equivalents to false.
+ return $context ? $context : FALSE;
+}
+
+/**
+ * Provide a nice administrative summary of the page so an admin can see at a
+ * glance what this page does and how it is configured.
+ */
+function page_manager_page_admin_summary($task, $subtask) {
+ $task_name = page_manager_make_task_name($task['name'], $subtask['name']);
+ $page = $subtask['subtask'];
+ $output = '';
+
+ $rows = array();
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Storage')),
+ array('class' => array('page-summary-data'), 'data' => $subtask['storage']),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ if (!empty($page->disabled)) {
+ $link = l(t('Enable'), page_manager_edit_url($task_name, array('handlers', $page->name, 'actions', 'enable')));
+ $text = t('Disabled');
+ }
+ else {
+ $link = l(t('Disable'), page_manager_edit_url($task_name, array('handlers', $page->name, 'actions', 'disable')));
+ $text = t('Enabled');
+ }
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Status')),
+ array('class' => array('page-summary-data'), 'data' => $text),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+
+ $path = array();
+ foreach (explode('/', $page->path) as $bit) {
+ if ($bit[0] != '!') {
+ $path[] = $bit;
+ }
+ }
+
+ $path = implode('/', $path);
+ $front = variable_get('site_frontpage', 'node');
+
+ $link = l(t('Edit'), page_manager_edit_url($task_name, array('settings', 'basic')));
+ $message = '';
+ if ($path == $front) {
+ $message = t('This is your site home page.');
+ }
+ else if (!empty($page->make_frontpage)) {
+ $message = t('This page is set to become your site home page.');
+ }
+
+ if ($message) {
+ $rows[] = array(
+ array('class' => array('page-summary-data'), 'data' => $message, 'colspan' => 2),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+ }
+
+ if (strpos($path, '%') === FALSE) {
+ $path = l('/' . $page->path, $path);
+ }
+ else {
+ $path = '/' . $page->path;
+ }
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Path')),
+ array('class' => array('page-summary-data'), 'data' => $path),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ if (empty($access['plugins'])) {
+ $access['plugins'] = array();
+ }
+
+ $contexts = page_manager_page_get_contexts($task, $subtask);
+ $access = ctools_access_group_summary($page->access, $contexts);
+ if ($access) {
+ $access = t('Accessible only if @conditions.', array('@conditions' => $access));
+ }
+ else {
+ $access = t('This page is publicly accessible.');
+ }
+
+ $link = l(t('Edit'), page_manager_edit_url($task_name, array('settings', 'access')));
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Access')),
+ array('class' => array('page-summary-data'), 'data' => $access),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ $menu_options = array(
+ 'none' => t('No menu entry.'),
+ 'normal' => t('Normal menu entry.'),
+ 'tab' => t('Menu tab.'),
+ 'default tab' => t('Default menu tab.'),
+ 'action' => t('Local action'),
+ );
+
+ if (!empty($page->menu)) {
+ $menu = $menu_options[$page->menu['type']];
+ if ($page->menu['type'] != 'none') {
+ $menu .= ' ' . t('Title: %title.', array('%title' => $page->menu['title']));
+ switch ($page->menu['type']) {
+ case 'default tab':
+ $menu .= ' ' . t('Parent title: %title.', array('%title' => $page->menu['parent']['title']));
+ break;
+ case 'normal':
+ if (module_exists('menu')) {
+ $menus = menu_get_menus();
+ $menu .= ' ' . t('Menu block: %title.', array('%title' => $menus[$page->menu['name']]));
+ }
+ break;
+ }
+ }
+ }
+ else {
+ $menu = t('No menu entry');
+ }
+
+ $link = l(t('Edit'), page_manager_edit_url($task_name, array('settings', 'menu')));
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Menu')),
+ array('class' => array('page-summary-data'), 'data' => $menu),
+ array('class' => array('page-summary-operation'), 'data' => $link),
+ );
+
+ $output .= theme('table', array('rows' => $rows, 'attributes' => array('id' => 'page-manager-page-summary')));
+ return $output;
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_page_enable(&$cache, $status) {
+ $page = &$cache->subtask['subtask'];
+ ctools_include('export');
+ ctools_export_set_object_status($page, $status);
+
+ $page->disabled = FALSE;
+}
+
+/**
+ * Recalculate the arguments when something like the path changes.
+ */
+function page_manager_page_recalculate_arguments(&$page) {
+ // Ensure $page->arguments contains only real arguments:
+ $arguments = page_manager_page_get_named_arguments($page->path);
+ $args = array();
+ foreach ($arguments as $keyword => $position) {
+ if (isset($page->arguments[$keyword])) {
+ $args[$keyword] = $page->arguments[$keyword];
+ }
+ else {
+ $args[$keyword] = array(
+ 'id' => '',
+ 'identifier' => '',
+ 'argument' => '',
+ 'settings' => array(),
+ );
+ }
+ }
+ $page->arguments = $args;
+}
+
+/**
+ * When adding or cloning a new page, this creates a new page cache
+ * and adds our page to it.
+ *
+ * This does not check to see if the existing cache is already locked.
+ * This must be done beforehand.
+ *
+ * @param &$page
+ * The page to create.
+ * @param &$cache
+ * The cache to use. If the cache has any existing task handlers,
+ * they will be marked for deletion. This may be a blank object.
+ */
+function page_manager_page_new_page_cache(&$page, &$cache) {
+ // Does a page already exist? If so, we are overwriting it so
+ // take its pid.
+ if (!empty($cache->subtask) && !empty($cache->subtask['subtask']) && !empty($cache->subtask['subtask']->pid)) {
+ $page->pid = $cache->subtask['subtask']->pid;
+ }
+ else {
+ $cache->new = TRUE;
+ }
+
+ $cache->task_name = page_manager_make_task_name('page', $page->name);
+ $cache->task_id = 'page';
+ $cache->task = page_manager_get_task('page');
+ $cache->subtask_id = $page->name;
+ $page->export_type = EXPORT_IN_DATABASE;
+ $page->type = t('Normal');
+ $cache->subtask = page_manager_page_build_subtask($cache->task, $page);
+
+ if (isset($cache->handlers)) {
+ foreach($cache->handlers as $id => $handler) {
+ $cache->handler_info[$id]['changed'] = PAGE_MANAGER_CHANGED_DELETED;
+ }
+ }
+ else {
+ $cache->handlers = array();
+ $cache->handler_info = array();
+ }
+
+ if (!empty($page->default_handlers)) {
+ foreach ($page->default_handlers as $id => $handler) {
+ page_manager_handler_add_to_page($cache, $handler);
+ }
+ }
+
+ $cache->locked = FALSE;
+ $cache->changed = TRUE;
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_page_access_check($task, $subtask_id, $contexts) {
+ $page = page_manager_page_load($subtask_id);
+ return ctools_access($page->access, $contexts);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/poll.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/poll.inc
new file mode 100644
index 000000000..073ee0c60
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/poll.inc
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_poll_page_manager_tasks() {
+ if (!module_exists('poll')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('All polls'),
+ 'admin title' => t('All polls'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for the polls at <em>/poll</em>. If no variant is selected, the default Drupal most recent polls will be shown.'),
+ 'admin path' => 'poll',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu alter' => 'page_manager_poll_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_poll_disabled', TRUE),
+ 'enable callback' => 'page_manager_poll_enable',
+ 'access callback' => 'page_manager_poll_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_poll_page_manager_tasks().
+ *
+ * Alter the node edit input so that node edit comes to us rather than the
+ * normal node edit process.
+ */
+function page_manager_poll_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_poll_disabled', TRUE)) {
+ return;
+ }
+
+ $callback = $items['poll']['page callback'];
+ // Override the node edit handler for our purpose.
+ if ($callback == 'poll_page' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['poll']['page callback'] = 'page_manager_poll';
+ $items['poll']['file path'] = $task['path'];
+ $items['poll']['file'] = $task['file'];
+ }
+ else {
+ variable_set('page_manager_poll_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_poll'])) {
+ drupal_set_message(t('Page manager module is unable to enable poll because some other module already has overridden with %callback.', array('%callback' => $callback)), 'warning');
+ }
+ return;
+ }
+
+}
+
+/**
+ * Entry point for our overridden node edit.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * node edit, which is node_page_edit().
+ */
+function page_manager_poll() {
+ // Load my task plugin
+ $task = page_manager_get_task('poll');
+
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $output = ctools_context_handler_render($task, '', array(), array());
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ module_load_include('inc', 'poll', 'poll.pages');
+ $function = 'poll_page';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('poll')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ return $function();
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_poll_enable($cache, $status) {
+ variable_set('page_manager_poll_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_poll'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_poll_access_check($task, $subtask_id, $contexts) {
+ return user_access('access content');
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc
new file mode 100644
index 000000000..efd7415c4
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc
@@ -0,0 +1,249 @@
+<?php
+
+/**
+ * @file
+ * Handle the 'node view' override task.
+ *
+ * This plugin overrides node/%node and reroutes it to the page manager, where
+ * a list of tasks can be used to service this request based upon criteria
+ * supplied by access plugins.
+ */
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_search_page_manager_tasks() {
+ if (!module_exists('search')) {
+ return;
+ }
+
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+ 'title' => t('Search'),
+
+ // There are multiple search pages, let's override each of them
+ // separately.
+ 'subtasks' => TRUE,
+ 'subtask callback' => 'page_manager_search_subtask',
+ 'subtasks callback' => 'page_manager_search_subtasks',
+
+ // Menu hooks so that we can alter the node/%node menu entry to point to us.
+ 'hook menu alter' => 'page_manager_search_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+ 'get arguments' => 'page_manager_search_get_arguments',
+ 'get context placeholders' => 'page_manager_search_get_contexts',
+ 'access callback' => 'page_manager_search_access_check',
+
+ );
+}
+
+/**
+ * Callback defined by page_manager_search_page_manager_tasks().
+ *
+ * Alter the search tabs to work with page manager. The search flow is
+ * quite odd, and tracing through the code takes hours to realize
+ * that the tab you click on does not normally actually handle
+ * the search. This tries to account for that.
+ *
+ * Note to module authors: This tends to work a lot better with modules
+ * that override their own search pages if their _alter runs *before*
+ * this one.
+ */
+function page_manager_search_menu_alter(&$items, $task) {
+ // We are creating two sets of tabs. One set is for searching without
+ // keywords. A second set is for searching *with* keywords. This
+ // is necessary because search/node/% and search/node need to be
+ // different due to the way the search menu items function.
+
+ $default_info = search_get_default_module_info();
+ if (empty($default_info)) {
+ // Nothing to do.
+ return;
+ }
+
+ // Go through each search module item.
+ foreach (search_get_info() as $module => $info) {
+ if (variable_get('page_manager_search_disabled_' . $module, TRUE)) {
+ continue;
+ }
+
+ $path = 'search/' . $info['path'];
+ $callback = $items["$path/%menu_tail"]['page callback'];
+
+ if ($callback == 'search_view' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items["$path"]['page callback'] = 'page_manager_search_page';
+ $items["$path"]['file path'] = $task['path'];
+ $items["$path"]['file'] = $task['file'];
+
+ $items["$path/%menu_tail"]['page callback'] = 'page_manager_search_page';
+ $items["$path/%menu_tail"]['file path'] = $task['path'];
+ $items["$path/%menu_tail"]['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_search_disabled_' . $module, TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_search'])) {
+ drupal_set_message(t('Page manager module is unable to enable @path because some other module already has overridden with %callback.', array('%callback' => $callback, '@path' => $path)), 'error');
+ }
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden search page.
+ *
+ */
+function page_manager_search_page($type) {
+ ctools_include('menu');
+// menu_set_active_trail(ctools_get_menu_trail('search/' . $type));
+
+ // Get the arguments and construct a keys string out of them.
+ $args = func_get_args();
+
+ // We have to remove the $type.
+ array_shift($args);
+
+ // And implode() it all back together.
+ $keys = $args ? implode('/', $args) : '';
+
+ // Allow other modules to alter the search keys
+ drupal_alter(array('search_keys', 'search_'. $type .'_keys'), $keys);
+
+ // Load my task plugin
+ $task = page_manager_get_task('search');
+ $subtask = page_manager_get_task_subtask($task, $type);
+
+ // Load the node into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, $subtask, array($keys));
+
+ $output = ctools_context_handler_render($task, $subtask, $contexts, array($keys));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ $function = 'search_view';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('search')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+
+ // Put the $type back on the arguments.
+ module_load_include('inc', 'search', 'search.pages');
+ array_unshift($args, $type);
+ return call_user_func_array($function, $args);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_search_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'keywords',
+ 'identifier' => t('Keywords'),
+ 'id' => 1,
+ 'name' => 'string',
+ 'settings' => array('use_tail' => TRUE),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_search_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_search_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_search_enable($cache, $status) {
+ variable_set('page_manager_search_disabled_' . $cache->subtask_id, $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_search'] = TRUE;
+ }
+}
+
+/**
+ * Task callback to get all subtasks.
+ *
+ * Return a list of all subtasks.
+ */
+function page_manager_search_subtasks($task) {
+ $return = array();
+ foreach (search_get_info() as $module => $info) {
+ if ($info['path']) {
+ // We don't pass the $info because the subtask build could be called
+ // singly without the $info when just the subtask needs to be built.
+ $return[$module] = page_manager_search_build_subtask($task, $module);
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Callback to return a single subtask.
+ */
+function page_manager_search_subtask($task, $subtask_id) {
+ return page_manager_search_build_subtask($task, $subtask_id);
+}
+
+/**
+ * Build a subtask array for a given page.
+ */
+function page_manager_search_build_subtask($task, $module) {
+ $search_info = search_get_info();
+ $info = $search_info[$module];
+ $path = 'search/' . $info['path'];
+ $subtask = array(
+ 'name' => $module,
+ 'admin title' => $info['title'],
+ 'admin path' => "$path/!keywords",
+ 'admin description' => t('Search @type', array('@type' => $info['title'])),
+ 'admin type' => t('System'),
+ 'row class' => empty($page->disabled) ? 'page-manager-enabled' : 'page-manager-disabled',
+ 'storage' => t('In code'),
+ 'disabled' => variable_get('page_manager_search_disabled_' . $module, TRUE),
+ // This works for both enable AND disable
+ 'enable callback' => 'page_manager_search_enable',
+ );
+
+ return $subtask;
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_search_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return _search_menu_access($context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc
new file mode 100644
index 000000000..37259b95b
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc
@@ -0,0 +1,377 @@
+<?php
+
+/**
+ * @file
+ * Handle the 'term view' override task.
+ *
+ * This plugin overrides term/%term and reroutes it to the page manager, where
+ * a list of tasks can be used to service this request based upon criteria
+ * supplied by access plugins.
+ */
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_term_view_page_manager_tasks() {
+ if (module_exists('taxonomy')) {
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+
+ 'title' => t('Taxonomy term template'),
+ 'admin title' => t('Taxonomy term template'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying taxonomy terms at <em>taxonomy/term/%term</em>. If you add variants, you may use selection criteria such as vocabulary or user access to provide different displays of the taxonomy term and associated nodes. If no variant is selected, the default Drupal taxonomy term display will be used. This page only affects items actually displayed ad taxonomy/term/%term. Some taxonomy terms, such as forums, have their displays moved elsewhere. Also please note that if you are using pathauto, aliases may make a taxonomy terms appear somewhere else, but as far as Drupal is concerned, they are still at taxonomy/term/%term.'),
+ 'admin path' => 'taxonomy/term/%taxonomy_term',
+ 'admin summary' => 'page_manager_term_view_admin_summary',
+
+ // Menu hooks so that we can alter the term/%term menu entry to point to us.
+ 'hook menu' => 'page_manager_term_view_menu',
+ 'hook menu alter' => 'page_manager_term_view_menu_alter',
+
+ // Provide a setting to the primary settings UI for Panels
+ 'admin settings' => 'page_manager_term_view_admin_settings',
+ // Even though we don't have subtasks, this allows us to save our settings.
+ 'save subtask callback' => 'page_manager_term_view_save',
+
+ // Callback to add items to the page manager task administration form:
+ 'task admin' => 'page_manager_term_view_task_admin',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context',
+ 'get arguments' => 'page_manager_term_view_get_arguments',
+ 'get context placeholders' => 'page_manager_term_view_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_term_view_disabled', TRUE),
+ 'enable callback' => 'page_manager_term_view_enable',
+ 'access callback' => 'page_manager_term_view_access_check',
+
+ // Allow additional operations
+ 'operations' => array(
+ 'settings' => array(
+ 'title' => t('Settings'),
+ 'description' => t('Edit name, path and other basic settings for the page.'),
+ 'form' => 'page_manager_term_view_settings',
+ ),
+ ),
+ );
+ }
+}
+
+/**
+ * Callback defined by page_manager_term_view_page_manager_tasks().
+ *
+ * Alter the term view input so that term view comes to us rather than the
+ * normal term view process.
+ */
+function page_manager_term_view_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_term_view_disabled', TRUE)) {
+ return;
+ }
+
+ // Override the term view handler for our purpose, but only if someone else
+ // has not already done so.
+ if (isset($items['taxonomy/term/%taxonomy_term']) && $items['taxonomy/term/%taxonomy_term']['page callback'] == 'taxonomy_term_page' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['taxonomy/term/%taxonomy_term']['page callback'] = 'page_manager_term_view_page';
+ $items['taxonomy/term/%taxonomy_term']['file path'] = $task['path'];
+ $items['taxonomy/term/%taxonomy_term']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_term_view_disabled', TRUE);
+
+ if (isset($items['taxonomy/term/%taxonomy_term']['page callback'])) {
+ $callback = $items['taxonomy/term/%taxonomy_term']['page callback'];
+ }
+ // Because Views changes %taxonomy_term to %views_arg, check to see if that
+ // is why we can't enable:
+ else if (isset($items['taxonomy/term/%views_arg']['page callback'])) {
+ $callback = $items['taxonomy/term/%views_arg']['page callback'];
+ }
+ else {
+ $callback = t('an unknown callback');
+ }
+ if (!empty($GLOBALS['page_manager_enabling_term_view'])) {
+ drupal_set_message(t('Page manager module is unable to enable taxonomy/term/%taxonomy_term because some other module already has overridden with %callback.', array('%callback' => $callback)), 'error');
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden term view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * term view, which is term_page_view().
+ */
+function page_manager_term_view_page($term, $depth = NULL) {
+ // Prep the term to be displayed so all of the regular hooks are triggered.
+ // Rather than calling taxonomy_term_page() directly, as it that would
+ // potentially load nodes that were not necessary, execute some of the code
+ // prior to identifying the correct CTools or Page Manager task handler and
+ // only proceed with the rest of the code if necessary.
+
+ // Assign the term name as the page title.
+ drupal_set_title($term->name);
+
+ // If there is a menu link to this term, the link becomes the last part
+ // of the active trail, and the link name becomes the page title.
+ // Thus, we must explicitly set the page title to be the node title.
+ $uri = entity_uri('taxonomy_term', $term);
+
+ // Set the term path as the canonical URL to prevent duplicate content.
+ drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE);
+ // Set the non-aliased path as a default shortlink.
+ drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
+
+ // Trigger the main
+ $build = taxonomy_term_show($term);
+
+ // Load my task plugin
+ $task = page_manager_get_task('term_view');
+
+ // Load the term into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($term, $depth));
+
+ if (empty($contexts)) {
+ return MENU_NOT_FOUND;
+ }
+
+ // Build the full output using the configured CTools plugin.
+ $output = ctools_context_handler_render($task, '', $contexts, array($term->tid));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ // Try loading an override plugin.
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('term_view')) && function_exists($rc)) {
+ return $rc($term, $depth);
+ }
+ }
+
+ // Otherwise, fall back to replicating the output normally generated by
+ // taxonomy_term_page().
+
+ // Build breadcrumb based on the hierarchy of the term.
+ $current = (object) array(
+ 'tid' => $term->tid,
+ );
+ // @todo This overrides any other possible breadcrumb and is a pure hard-coded
+ // presumption. Make this behavior configurable per vocabulary or term.
+ $breadcrumb = array();
+ while ($parents = taxonomy_get_parents($current->tid)) {
+ $current = array_shift($parents);
+ $breadcrumb[] = l($current->name, 'taxonomy/term/' . $current->tid);
+ }
+ $breadcrumb[] = l(t('Home'), NULL);
+ $breadcrumb = array_reverse($breadcrumb);
+ drupal_set_breadcrumb($breadcrumb);
+ drupal_add_feed('taxonomy/term/' . $term->tid . '/feed', 'RSS - ' . $term->name);
+
+ if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) {
+ $nodes = node_load_multiple($nids);
+ $build += node_view_multiple($nodes);
+ $build['pager'] = array(
+ '#theme' => 'pager',
+ '#weight' => 5,
+ );
+ }
+ else {
+ $build['no_content'] = array(
+ '#prefix' => '<p>',
+ '#markup' => t('There is currently no content classified with this term.'),
+ '#suffix' => '</p>',
+ );
+ }
+ return $build;
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the term view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_term_view_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'term',
+ 'identifier' => page_manager_term_view_get_type() == 'multiple' ? t('Term(s) being viewed') : t('Term being viewed'),
+ 'id' => 1,
+ 'name' => page_manager_term_view_get_type() == 'multiple' ? 'terms' : 'term',
+ 'settings' => array('input_form' => 'tid', 'breadcrumb' => variable_get('page_manager_taxonomy_breadcrumb', TRUE)),
+ 'default' => '404',
+ ),
+ array(
+ 'keyword' => 'depth',
+ 'identifier' => t('Depth'),
+ 'id' => 1,
+ 'name' => 'string',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_term_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_term_view_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Settings page for this item.
+ */
+function page_manager_term_view_settings($form, &$form_state) {
+ // This passes thru because the setting can also appear on the main Panels
+ // settings form. If $settings is an array it will just pick up the default.
+ $settings = isset($form_state->update_values) ? $form_state->update_values : array();
+ return page_manager_term_view_admin_settings($form, $settings);
+}
+
+/**
+ * Copy form values into the page cache.
+ */
+function page_manager_term_view_settings_submit(&$form, &$form_state) {
+ $form_state['page']->update_values = $form_state['values'];
+}
+
+/**
+ * Save when the page cache is saved.
+ */
+function page_manager_term_view_save($subtask, $cache) {
+ if (isset($cache->update_values)) {
+ variable_set('page_manager_term_view_type', $cache->update_values['page_manager_term_view_type']);
+ variable_set('page_manager_taxonomy_breadcrumb', $cache->update_values['page_manager_taxonomy_breadcrumb']);
+ }
+}
+
+/**
+ * Provide a setting to the Panels administrative form.
+ */
+function page_manager_term_view_admin_settings($form, $settings = array()) {
+ if (empty($settings)) {
+ $settings = array(
+ 'page_manager_term_view_type' => page_manager_term_view_get_type(),
+ 'page_manager_taxonomy_breadcrumb' => variable_get('page_manager_taxonomy_breadcrumb', TRUE),
+ );
+ }
+
+ $form['page_manager_term_view_type'] = array(
+ '#type' => 'radios',
+ '#title' => t('Allow multiple terms on taxonomy/term/%term'),
+ '#options' => array('single' => t('Single term'), 'multiple' => t('Multiple terms')),
+ '#description' => t('By default, Drupal allows multiple terms as an argument by separating them with commas or plus signs. If you set this to single, that feature will be disabled.') . ' ' . t('This feature does not currently work and is disabled.'),
+ '#default_value' => $settings['page_manager_term_view_type'],
+ // @todo -- fix this
+ '#disabled' => TRUE,
+ );
+ $form['page_manager_taxonomy_breadcrumb'] = array(
+ '#title' => t('Inject hierarchy of first term into breadcrumb trail'),
+ '#type' => 'checkbox',
+ '#default_value' => $settings['page_manager_taxonomy_breadcrumb'],
+ '#description' => t('If checked, taxonomy term parents will appear in the breadcrumb trail.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_term_view_enable($cache, $status) {
+ variable_set('page_manager_term_view_disabled', $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_term_view'] = TRUE;
+ }
+}
+
+function page_manager_term_view_get_type() {
+// $view_type = variable_get('page_manager_term_view_type', 'multiple');
+ // Revert to just allowing single.
+ $view_type = 'single';
+
+ return $view_type;
+}
+
+/**
+ * Provide a nice administrative summary of the page so an admin can see at a
+ * glance what this page does and how it is configured.
+ */
+function page_manager_term_view_admin_summary($task, $subtask) {
+ $task_name = page_manager_make_task_name($task['name'], $subtask['name']);
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Path')),
+ array('class' => array('page-summary-data'), 'data' => 'taxonomy/term/%term'),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Access')),
+ array('class' => array('page-summary-data'), 'data' => t('This page is publicly accessible.')),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ $menu = t('No menu entry');
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Menu')),
+ array('class' => array('page-summary-data'), 'data' => $menu),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ if (page_manager_term_view_get_type() == 'multiple') {
+ $message = t('Multiple terms may be used, separated by , or +.');
+ }
+ else {
+ $message = t('Only a single term may be used.');
+ }
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('%term')),
+ array('class' => array('page-summary-data'), 'data' => $message),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ if (variable_get('page_manager_taxonomy_breadcrumb', TRUE)) {
+ $message = t('Breadcrumb trail will contain taxonomy term hierarchy');
+ }
+ else {
+ $message = t('Breadcrumb trail will not contain taxonomy term hiearchy.');
+ }
+
+ $rows[] = array(
+ array('class' => array('page-summary-label'), 'data' => t('Breadcrumb')),
+ array('class' => array('page-summary-data'), 'data' => $message),
+ array('class' => array('page-summary-operation'), 'data' => ''),
+ );
+
+ $output = theme('table', array(), $rows, array('id' => 'page-manager-page-summary'));
+ return $output;
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_term_view_access_check($task, $subtask_id, $contexts) {
+ return user_access('access content');
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/user_edit.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/user_edit.inc
new file mode 100644
index 000000000..0b11bf017
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/user_edit.inc
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * Overrides the user profile display at user/%user.
+ *
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_user_edit_page_manager_tasks() {
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+ 'title' => t('User Edit Template'),
+ 'admin title' => t('User edit template'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying user edit form at <em>user/%user/edit</em>.'),
+ 'admin path' => 'user/%user/edit',
+
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_user_edit_task_admin',
+
+ 'hook menu' => 'page_manager_user_edit_menu',
+ 'hook menu alter' => 'page_manager_user_edit_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context', // handler type -- misnamed
+ 'get arguments' => 'page_manager_user_edit_get_arguments',
+ 'get context placeholders' => 'page_manager_user_edit_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_user_edit_disabled', TRUE),
+ 'enable callback' => 'page_manager_user_edit_enable',
+ 'access callback' => 'page_manager_user_edit_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_user_view_page_manager_tasks().
+ *
+ * Alter the user view input so that user view comes to us rather than the
+ * normal user view process.
+ */
+function page_manager_user_edit_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_user_edit_disabled', TRUE)) {
+ return;
+ }
+
+ // Override the user view handler for our purpose.
+ if ($items['user/%user/edit']['page callback'] == 'drupal_get_form' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['user/%user/edit']['page callback'] = 'page_manager_user_edit_page';
+ $items['user/%user/edit']['page arguments'] = array(1);
+ $items['user/%user/edit']['file path'] = $task['path'];
+ $items['user/%user/edit']['file'] = $task['file'];
+ if (($categories = _user_categories()) && (count($categories) > 1)) {
+ foreach ($categories as $key => $category) {
+ // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
+ if ($category['name'] != 'account') {
+ $items['user/%user_category/edit/' . $category['name']]['page callback'] = 'page_manager_user_edit_page';
+ $items['user/%user_category/edit/' . $category['name']]['page arguments'] = array(1, 3);
+ $items['user/%user_category/edit/' . $category['name']]['file path'] = $task['path'];
+ $items['user/%user_category/edit/' . $category['name']]['file'] = $task['file'];
+ }
+ }
+ }
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_user_edit_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_user_edit'])) {
+ drupal_set_message(t('Page manager module is unable to enable user/%user/edit because some other module already has overridden with %callback.', array('%callback' => $items['user/%user']['page callback'])), 'error');
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden user view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * user edit, which is drupal_get_form('user_profile_form',$account).
+ */
+function page_manager_user_edit_page($account, $category = 'account') {
+ // Store the category on the user for later usage.
+ $account->user_category = $category;
+
+ // Load my task plugin:
+ $task = page_manager_get_task('user_edit');
+
+ // Load the account into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($account));
+ // Build content. @todo -- this may not be right.
+ user_build_content($account);
+
+ $output = ctools_context_handler_render($task, '', $contexts, array($account->uid));
+ if (is_array($output)) {
+ $output = drupal_render($output);
+ }
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ $function = 'drupal_get_form';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('user_edit')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ if ($function == 'drupal_get_form') {
+
+ //In order to ajax fields to work we need to run form_load_include.
+ //Hence we eschew drupal_get_form and manually build the info and
+ //call drupal_build_form.
+ $form_state = array();
+ $form_id = 'user_profile_form';
+ $args = array($account, $category);
+ $form_state['build_info']['args'] = $args;
+ form_load_include($form_state, 'inc', 'user', 'user.pages');
+ $output = drupal_build_form($form_id, $form_state);
+ return $output;
+ }
+ //fire off "view" op so that triggers still work
+ // @todo -- this doesn't work anymore, and the alternatives seem bad.
+ // will have to figure out how to fix this.
+ // user_module_invoke('view', $array = array(), $account);
+ return $function($account);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_user_edit_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'user',
+ 'identifier' => t('User being edited'),
+ 'id' => 1,
+ 'name' => 'user_edit',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_user_edit_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_user_edit_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_user_edit_enable($cache, $status) {
+ variable_set('page_manager_user_edit_disabled', $status);
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_user_edit'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_user_edit_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return user_edit_access($context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/plugins/tasks/user_view.inc b/sites/all/modules/ctools/page_manager/plugins/tasks/user_view.inc
new file mode 100644
index 000000000..c428384a1
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/plugins/tasks/user_view.inc
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * Overrides the user profile display at user/%user.
+ *
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_user_view_page_manager_tasks() {
+ return array(
+ // This is a 'page' task and will fall under the page admin UI
+ 'task type' => 'page',
+ 'title' => t('User profile template'),
+ 'admin title' => t('User profile template'),
+ 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying user profiles at <em>user/%user</em>. If you add variants, you may use selection criteria such as roles or user access to provide different views of user profiles. If no variant is selected, the default Drupal user view will be used. Please note that if you are using pathauto, aliases may make a node to be somewhere else, but as far as Drupal is concerned, they are still at user/%user.'),
+ 'admin path' => 'user/%user',
+
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_user_view_task_admin',
+
+ 'hook menu' => 'page_manager_user_view_menu',
+ 'hook menu alter' => 'page_manager_user_view_menu_alter',
+
+ // This is task uses 'context' handlers and must implement these to give the
+ // handler data it needs.
+ 'handler type' => 'context', // handler type -- misnamed
+ 'get arguments' => 'page_manager_user_view_get_arguments',
+ 'get context placeholders' => 'page_manager_user_view_get_contexts',
+
+ // Allow this to be enabled or disabled:
+ 'disabled' => variable_get('page_manager_user_view_disabled', TRUE),
+ 'enable callback' => 'page_manager_user_view_enable',
+ 'access callback' => 'page_manager_user_view_access_check',
+ );
+}
+
+/**
+ * Callback defined by page_manager_user_view_page_manager_tasks().
+ *
+ * Alter the user view input so that user view comes to us rather than the
+ * normal user view process.
+ */
+function page_manager_user_view_menu_alter(&$items, $task) {
+ if (variable_get('page_manager_user_view_disabled', TRUE)) {
+ return;
+ }
+
+ // Override the user view handler for our purpose.
+ if ($items['user/%user']['page callback'] == 'user_view_page' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['user/%user']['page callback'] = 'page_manager_user_view_page';
+ $items['user/%user']['file path'] = $task['path'];
+ $items['user/%user']['file'] = $task['file'];
+ }
+ else {
+ // automatically disable this task if it cannot be enabled.
+ variable_set('page_manager_user_view_disabled', TRUE);
+ if (!empty($GLOBALS['page_manager_enabling_user_view'])) {
+ drupal_set_message(t('Page manager module is unable to enable user/%user because some other module already has overridden with %callback.', array('%callback' => $items['user/%user']['page callback'])), 'error');
+ }
+ }
+}
+
+/**
+ * Entry point for our overridden user view.
+ *
+ * This function asks its assigned handlers who, if anyone, would like
+ * to run with it. If no one does, it passes through to Drupal core's
+ * user view, which is user_page_view().
+ */
+function page_manager_user_view_page($account) {
+ // Load my task plugin:
+ $task = page_manager_get_task('user_view');
+
+ // Load the account into a context.
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $contexts = ctools_context_handler_get_task_contexts($task, '', array($account));
+
+ // Build content. @todo -- this may not be right.
+ user_build_content($account);
+
+ $output = ctools_context_handler_render($task, '', $contexts, array($account->uid));
+ if ($output !== FALSE) {
+ return $output;
+ }
+
+ $function = 'user_view';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
+ if (($rc = $call('user_view')) && function_exists($rc)) {
+ $function = $rc;
+ break;
+ }
+ }
+
+ // Otherwise, fall back.
+ if ($function == 'user_view') {
+ module_load_include('inc', 'user', 'user.pages');
+ }
+ //fire off "view" op so that triggers still work
+ // @todo -- this doesn't work anymore, and the alternatives seem bad.
+ // will have to figure out how to fix this.
+// user_module_invoke('view', $array = array(), $account);
+ return $function($account);
+}
+
+/**
+ * Callback to get arguments provided by this task handler.
+ *
+ * Since this is the node view and there is no UI on the arguments, we
+ * create dummy arguments that contain the needed data.
+ */
+function page_manager_user_view_get_arguments($task, $subtask_id) {
+ return array(
+ array(
+ 'keyword' => 'user',
+ 'identifier' => t('User being viewed'),
+ 'id' => 1,
+ 'name' => 'entity_id:user',
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Callback to get context placeholders provided by this handler.
+ */
+function page_manager_user_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_user_view_get_arguments($task, $subtask_id));
+}
+
+/**
+ * Callback to enable/disable the page from the UI.
+ */
+function page_manager_user_view_enable($cache, $status) {
+ variable_set('page_manager_user_view_disabled', $status);
+
+ // Set a global flag so that the menu routine knows it needs
+ // to set a message if enabling cannot be done.
+ if (!$status) {
+ $GLOBALS['page_manager_enabling_user_view'] = TRUE;
+ }
+}
+
+/**
+ * Callback to determine if a page is accessible.
+ *
+ * @param $task
+ * The task plugin.
+ * @param $subtask_id
+ * The subtask id
+ * @param $contexts
+ * The contexts loaded for the task.
+ * @return
+ * TRUE if the current user can access the page.
+ */
+function page_manager_user_view_access_check($task, $subtask_id, $contexts) {
+ $context = reset($contexts);
+ return user_view_access($context->data);
+}
diff --git a/sites/all/modules/ctools/page_manager/theme/page-manager-edit-page.tpl.php b/sites/all/modules/ctools/page_manager/theme/page-manager-edit-page.tpl.php
new file mode 100644
index 000000000..85510cd93
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/theme/page-manager-edit-page.tpl.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @file
+ * Template for the page manager page editor.
+ *
+ * Variables available:
+ * -
+ *
+ * For javascript purposes the id must not change.
+ */
+?>
+<div id="page-manager-edit">
+ <?php print $locked; ?>
+ <div class="page-manager-wrapper">
+ <?php if (isset($operations['primary'])): ?>
+ <div class="primary-actions clearfix actions">
+ <?php print $operations['primary']; ?>
+ </div>
+ <?php endif; ?>
+ <div class="page-manager-tabs clearfix">
+ <div class="page-manager-edit-operations">
+ <div class="inside">
+ <?php print $operations['nav']; ?>
+ </div>
+ </div>
+ <div class="page-manager-ajax-pad">
+ <div class="inside">
+ <div class="content-header">
+ <div class="content-title">
+ <?php print $changed; ?>
+ <?php print $content['title']; ?>
+ </div>
+ <?php if (isset($operations['secondary'])): ?>
+ <div class="secondary-actions clearfix actions">
+ <?php print $operations['secondary']; ?>
+ </div>
+ <?php endif; ?>
+ </div>
+
+ <div class="content-content">
+ <?php if (!empty($content['description'])): ?>
+ <div class="description">
+ <?php print $content['description']; ?>
+ </div>
+ <?php endif; ?>
+ <?php print $content['content']; ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <?php print $save; ?>
+</div> \ No newline at end of file
diff --git a/sites/all/modules/ctools/page_manager/theme/page_manager.theme.inc b/sites/all/modules/ctools/page_manager/theme/page_manager.theme.inc
new file mode 100644
index 000000000..6435d1ce5
--- /dev/null
+++ b/sites/all/modules/ctools/page_manager/theme/page_manager.theme.inc
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Preprocess functions for page manager editing templates.
+ */
+
+/**
+ * Preprocess the page manager edit page.
+ */
+function template_preprocess_page_manager_edit_page(&$vars) {
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_modal_add_js();
+ ctools_add_js('dependent');
+
+ ctools_add_css('page-manager', 'page_manager');
+ ctools_add_css('wizard');
+
+ $task = $vars['page']->task;
+
+ $page = &$vars['page'];
+
+ $vars['locked'] = '';
+ $vars['changed'] = '';
+ if (!empty($page->locked)) {
+ $vars['locked'] = theme('page_manager_lock', array('page' => $page));
+ $vars['changed'] = theme('page_manager_changed', array('text' => t('Locked'), 'description' => t('This page is being edited by another user and you cannot make changes to it.')));
+ }
+ else if (!empty($page->new)) {
+ $vars['changed'] = theme('page_manager_changed', array('text' => t('New'), 'description' => t('This page is newly created and has not yet been saved to the database. It will not be available until you save it.')));
+ }
+ else if (!empty($page->changed)) {
+ $vars['changed'] = theme('page_manager_changed', array('text' => t('Changed'), 'description' => t('This page has been modified, but these modifications are not yet live. While modifying this page, it is locked from modification by other users.')));
+ }
+
+ $form_state = array(
+ 'page' => &$vars['page'],
+ );
+
+ $active = $vars['content']['active'];
+ if ($active[0] == 'handlers' && isset($vars['operations'][$active[1]])) {
+ $vars['operations']['secondary'] = $vars['operations'][$active[1]];
+ }
+}
+
+/**
+ * Turn the rearrange form into a table with tablesorting on.
+ */
+function theme_page_manager_handler_rearrange($vars) {
+ $form = &$vars['form'];
+ // Assemble the data for a table from everything in $form['handlers']
+ foreach (element_children($form['handlers']) as $id) {
+ // provide a reference shortcut.
+ $element = &$form['handlers'][$id];
+ if (isset($element['title'])) {
+ $row = array();
+
+ $row[] = array(
+ 'data' => render($element['title']),
+ 'class' => array('page-manager-handler'),
+ );
+
+ $element['weight']['#attributes']['class'][] = 'weight';
+ $row[] = render($element['weight']);
+
+ $rows[] = array('data' => $row, 'class' => array('draggable'), 'id' => 'page-manager-row-' . $id);
+ }
+ }
+
+ if (empty($rows)) {
+ $rows[] = array(array('data' => t('No task handlers are defined for this task.'), 'colspan' => '5'));
+ }
+
+ $header = array(
+ array('data' => t('Variant'), 'class' => array('page-manager-handler')),
+ t('Weight'),
+ );
+
+ drupal_add_tabledrag('page-manager-arrange-handlers', 'order', 'sibling', 'weight');
+
+ $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'page-manager-arrange-handlers')));
+ $output .= drupal_render_children($form);
+ return $output;
+}
+
+/**
+ * Draw the "this task is locked from editing" box.
+ */
+function theme_page_manager_lock($vars) {
+ $page = $vars['page'];
+
+ $account = user_load($page->locked->uid);
+ $name = theme('username', array('account' => $account));
+ $lock_age = format_interval(REQUEST_TIME - $page->locked->updated);
+ $break = url(page_manager_edit_url($page->task_name, array('actions', 'break-lock')));
+
+ ctools_add_css('ctools');
+ $output = '<div class="ctools-locked">';
+ $output .= t('This page is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break));
+ $output .= '</div>';
+ return $output;
+}
+
+/**
+ * Draw the "you have unsaved changes and this task is locked." message.
+ */
+function theme_page_manager_changed($vars) {
+ $text = $vars['text'];
+ $description = $vars['description'];
+
+ ctools_add_css('ctools');
+ $output = '<div class="page-manager-changed" title="' . $description . '">';
+ $output .= $text;
+ $output .= '</div>';
+
+ return $output;
+}