diff --git a/plugins/content_types/rocketship_legend.inc b/plugins/content_types/rocketship_legend.inc
new file mode 100644
index 0000000..f19ddbc
--- /dev/null
+++ b/plugins/content_types/rocketship_legend.inc
@@ -0,0 +1,48 @@
+ TRUE,
+ 'title' => t('Rocketship Legend'),
+ 'description' => t('Shows a legend for the Rocketship issue colors.'),
+ 'category' => t('Miscellaneous'),
+ 'edit form' => 'rocketship_legend_form',
+ 'render callback' => 'rocketship_legend_render',
+ 'admin info' => 'rocketship_legend_admin_info',
+);
+
+/**
+ * Info callback for the Rocketship legend panel.
+ */
+function rocketship_legend_admin_info($subtype, $conf, $contexts) {
+ if (!empty($conf)) {
+ $block = new stdClass;
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
+ $block->content = t('Showing a link to the drupal issue queue.');
+ return $block;
+ }
+}
+
+/**
+ * Configuration form for the Rocketship legend panel.
+ */
+function rocketship_legend_form($form, &$form_state) {
+ return $form;
+}
+
+/**
+ * Renders the Rocketship legend panel.
+ */
+function rocketship_legend_render($subtype, $conf, $panel_args, $context = NULL) {
+ $block = new stdClass();
+
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : t('Legend');
+ $block->content = '
' . t('Critical issue') . '
';
+ $block->content .= '' . t('Major issue') . '
';
+ $block->content .= '' . t('Current top priority') . '
';
+
+ return $block;
+}
\ No newline at end of file
diff --git a/plugins/content_types/rocketship_link.inc b/plugins/content_types/rocketship_link.inc
new file mode 100644
index 0000000..cbbebbc
--- /dev/null
+++ b/plugins/content_types/rocketship_link.inc
@@ -0,0 +1,57 @@
+ TRUE,
+ 'title' => t('Rocketship Link'),
+ 'description' => t('Shows a link for certain tags to the drupal.org issue queue.'),
+ 'category' => t('Miscellaneous'),
+ 'edit form' => 'rocketship_link_form',
+ 'render callback' => 'rocketship_link_render',
+ 'admin info' => 'rocketship_link_admin_info',
+ 'required context' => new ctools_context_optional(t('Term'), 'entity:taxonomy_term'),
+);
+
+/**
+ * Info callback for the Rocketship link panel.
+ */
+function rocketship_link_admin_info($subtype, $conf, $contexts) {
+ if (!empty($conf)) {
+ $block = new stdClass;
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
+ $block->content = t('Showing a link to the drupal issue queue.');
+ return $block;
+ }
+}
+
+/**
+ * Configuration form for the Rocketship link panel.
+ */
+function rocketship_link_form($form, &$form_state) {
+ return $form;
+}
+
+/**
+ * Renders the Rocketship link panel.
+ */
+function rocketship_link_render($subtype, $conf, $panel_args, $context = NULL) {
+ $tags = array();
+ if (!empty($context) && !empty($context->data) && is_object($context->data)) {
+ $tags[$context->data->tid] = $context->data->name;
+ }
+ $path = rocketship_get_issue_path($tags);
+
+ $block = new stdClass();
+
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
+
+ $block->content = t('See all these issues also at !link. This view of issues is automatically cached and might be out of date up to two hours at times.', array(
+ '!link' => l(url($path['path'], $path), $path['path'], $path),
+ ));
+
+ return $block;
+}
diff --git a/plugins/content_types/rocketship_thanks.inc b/plugins/content_types/rocketship_thanks.inc
new file mode 100644
index 0000000..d80f72a
--- /dev/null
+++ b/plugins/content_types/rocketship_thanks.inc
@@ -0,0 +1,91 @@
+ TRUE,
+ 'title' => t('Rocketship Thanks'),
+ 'description' => t('Shows a tag cloud of the contributors.'),
+ 'category' => t('Miscellaneous'),
+ 'edit form' => 'rocketship_thanks_form',
+ 'render callback' => 'rocketship_thanks_render',
+ 'admin info' => 'rocketship_thanks_admin_info',
+);
+
+/**
+ * Info callback for the Rocketship legend panel.
+ */
+function rocketship_thanks_admin_info($subtype, $conf, $contexts) {
+ if (!empty($conf)) {
+ $block = new stdClass;
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
+ $block->content = t('Tag cloud of the contributors.');
+ return $block;
+ }
+}
+
+/**
+ * Configuration form for the Rocketship legend panel.
+ */
+function rocketship_thanks_form($form, &$form_state) {
+ return $form;
+}
+
+/**
+ * Renders the Rocketship legend panel.
+ */
+function rocketship_thanks_render($subtype, $conf, $panel_args, $context = NULL) {
+ $block = new stdClass();
+
+ $block->title = $conf['override_title'] ? $conf['override_title_text'] : t('Thanks to');
+
+
+ // Include username cloud if the sprint tag is displayed.
+ $participant_stats = variable_get('rocketship_participant_stats', array());
+ if (!empty($participant_stats)) {
+ $lead_users = variable_get('rocketship_lead_user', array('Gábor Hojtsy'));
+ $participant_uids = variable_get('rocketship_participant_uids', array());
+ $participant_recent = variable_get('rocketship_participant_recent', array());
+
+ // Remove lead user and some users to avoid skewing the stats.
+ if (!empty($lead_users) && is_array($lead_users)) {
+ foreach ($lead_users as $lead_user) {
+ unset($participant_stats[$lead_user]);
+ }
+ }
+
+ unset($participant_stats['System Message']);
+ unset($participant_stats['Abandoned Projects']);
+
+ $usercloud = array();
+ $max = max(array_values($participant_stats));
+ $steps = array(1, 2, 3, 5, 7, 10);
+ foreach ($steps as $i => $step) {
+ foreach ($participant_stats as $name => $count) {
+ if (($step == 10) || ($count <= $step * ($max/10))) {
+ $class = 'rocketship-cloud-' . ($i+1);
+ if (in_array($participant_uids[$name], $participant_recent)) {
+ $class .= ' rocketship-cloud-recent';
+ }
+ $usercloud[$name] = '' . check_plain($name) . '';
+ unset($participant_stats[$name]);
+ }
+ }
+ }
+ // Sort by username in a case insensitive fashion.
+ uksort($usercloud, "strnatcasecmp");
+ $notes = array();
+ $notes[] = t('Based on participation in all @tag issues (font size) using a quasi-logarithmic scale and on recent activity (boldness).', array('@tag' => variable_get('rocketship_master_tag', 'D8MI')));
+ if (!empty($lead_users)) {
+ $notes[] = format_plural(count($lead_users), 'Excludes the initiative lead, @lead.', 'Excludes the initiative leads, @lead.', array('@lead' => implode(', ', $lead_users)));
+ }
+
+ $block->content = '' . join(' ', $usercloud) . '
';
+ $block->content .= '' . join(' ', $notes) . '
';
+ }
+
+ return $block;
+}
\ No newline at end of file
diff --git a/plugins/layouts/rocketship/rocketship.css b/plugins/layouts/rocketship/rocketship.css
new file mode 100644
index 0000000..426117b
--- /dev/null
+++ b/plugins/layouts/rocketship/rocketship.css
@@ -0,0 +1,143 @@
+.rocketship .panel-col-top,
+.rocketship .panel-col-middle,
+.rocketship .panel-col-bottom,
+.rocketship .panel-separator {
+ width: 100%;
+ clear: both;
+}
+
+.rocketship .panel-col-top .inside,
+.rocketship .panel-col-middle .inside {
+ margin-bottom: 1em;
+}
+
+.rocketship .panel-col-first {
+ float: left;
+ width: 33%;
+}
+
+.rocketship .panel-col-first .inside {
+ margin: 0 .5em 1em 0;
+}
+
+.rocketship .panel-col {
+ float: left;
+ width: 33%;
+}
+
+.rocketship .panel-col .inside {
+ margin: 0 .5em 1em .5em;
+}
+
+.rocketship .panel-col-last {
+ float: left;
+ width: 33%;
+}
+
+.rocketship .panel-col-last .inside {
+ margin: 0 0 1em .5em;
+}
+
+.rocketship .panel-separator {
+ margin: 0 0 1em 0;
+}
+
+.view-id-issues .views-row {
+ margin-bottom: 1em;
+}
+
+.rocketship-node,
+.panel-pane .view-id-issues .node-rocketship-issue {
+ padding: 0.2em 0.3em 0.2em 0.5em;
+ -moz-box-shadow: 0 0 0.5em rgba(0,0,0,0.4);
+ -webkit-box-shadow: 0 0 0.5em rgba(0,0,0,0.4);
+ border: none;
+ border-left: 0.7em solid #F5F5F5;
+}
+
+.panel-pane .view-id-issues .node-rocketship-issue h2 a {
+ font-weight: bold;
+ font-size: 0.8em;
+}
+
+.rocketship-focus,
+.panel-pane .view-id-issues .node-rocketship-issue-focus {
+ border-left-color: #336699;
+}
+
+.rocketship-major,
+.panel-pane .view-id-issues .node-rocketship-issue-major {
+ border-left-color: #FF9900;
+}
+
+.rocketship-critical,
+.panel-pane .view-id-issues .node-rocketship-issue-critical {
+ border-left-color: #FF0000;
+}
+
+.rocketship-needs-tests,
+.panel-pane .view-id-issues .node-rocketship-issue-needs-tests {
+ font-weight: bold;
+}
+
+.view-id-issues .node-rocketship-issue .field-type-taxonomy-term-reference li {
+ background-color: #F5F5F5;
+ margin: 0.1em 0.3em 0.1em 0;
+ padding: 0.1em 0.3em;
+ display: inline-block;
+ border-radius: 0.2em;
+}
+
+.pane-rocketship-legend {
+ float: right;
+ clear: both;
+}
+.pane-rocketship-legend .pane-title,
+.pane-rocketship-legend .pane-content,
+.pane-rocketship-legend .rocketship-node {
+ display: inline-block;
+ margin-left: 6px;
+}
+.panel-pane .view-id-issues .node-rocketship-issue .field-name-rocketship-assignee {
+ background-color: #F5F5F5;
+ margin: -0.2em -0.3em -0.3em -0.5em;
+ padding: 0.1em 1em;
+ border: none;
+ border-top: 0.1em dotted #DDDDDD;
+ font-size: 0.9em;
+}
+.rocketship-cloud-wrapper {
+ border-top: 0.1em solid #3b3b3b;
+ margin-bottom: 2em;
+ clear: both;
+}
+.rocketship-cloud-wrapper p {
+ font-size: 0.8em;
+ margin-top: 1.3em;
+ font-style: italic;
+}
+.rocketship-cloud {
+ line-height: 1.3em;
+ text-align: justify;
+}
+.rocketship-cloud-1 {
+ font-size: 0.8em;
+}
+.rocketship-cloud-2 {
+ font-size: 1em;
+}
+.rocketship-cloud-3 {
+ font-size: 1.4em;
+}
+.rocketship-cloud-4 {
+ font-size: 1.6em;
+}
+.rocketship-cloud-5 {
+ font-size: 1.8em;
+}
+.rocketship-cloud-6 {
+ font-size: 2em;
+}
+.rocketship-cloud-recent a {
+ font-weight: bold;
+}
diff --git a/plugins/layouts/rocketship/rocketship.inc b/plugins/layouts/rocketship/rocketship.inc
new file mode 100644
index 0000000..412c329
--- /dev/null
+++ b/plugins/layouts/rocketship/rocketship.inc
@@ -0,0 +1,25 @@
+ t('Rocketship'),
+ 'category' => t('Columns: 3'),
+ 'icon' => 'rocketship.png',
+ 'theme' => 'rocketship',
+ 'css' => 'rocketship.css',
+ 'regions' => array(
+ 'top' => t('Top'),
+ 'upper_first' => t('Upper First'),
+ 'upper_middle' => t('Upper Middle'),
+ 'upper_last' => t('Upper Last'),
+ 'middle' => t('Middle'),
+ 'lower_first' => t('Lower First'),
+ 'lower_middle' => t('Lower Middle'),
+ 'lower_last' => t('Lower Last'),
+ 'bottom' => t('Bottom')
+ ),
+);
diff --git a/plugins/layouts/rocketship/rocketship.tpl.php b/plugins/layouts/rocketship/rocketship.tpl.php
new file mode 100644
index 0000000..18b6fc2
--- /dev/null
+++ b/plugins/layouts/rocketship/rocketship.tpl.php
@@ -0,0 +1,64 @@
+
+
diff --git a/rocketship-panel-and-views.patch b/rocketship-panel-and-views.patch
new file mode 100644
index 0000000..e7af327
--- /dev/null
+++ b/rocketship-panel-and-views.patch
@@ -0,0 +1,1847 @@
+diff --git a/plugins/content_types/rocketship_legend.inc b/plugins/content_types/rocketship_legend.inc
+new file mode 100644
+index 0000000..f19ddbc
+--- /dev/null
++++ b/plugins/content_types/rocketship_legend.inc
+@@ -0,0 +1,48 @@
++ TRUE,
++ 'title' => t('Rocketship Legend'),
++ 'description' => t('Shows a legend for the Rocketship issue colors.'),
++ 'category' => t('Miscellaneous'),
++ 'edit form' => 'rocketship_legend_form',
++ 'render callback' => 'rocketship_legend_render',
++ 'admin info' => 'rocketship_legend_admin_info',
++);
++
++/**
++ * Info callback for the Rocketship legend panel.
++ */
++function rocketship_legend_admin_info($subtype, $conf, $contexts) {
++ if (!empty($conf)) {
++ $block = new stdClass;
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
++ $block->content = t('Showing a link to the drupal issue queue.');
++ return $block;
++ }
++}
++
++/**
++ * Configuration form for the Rocketship legend panel.
++ */
++function rocketship_legend_form($form, &$form_state) {
++ return $form;
++}
++
++/**
++ * Renders the Rocketship legend panel.
++ */
++function rocketship_legend_render($subtype, $conf, $panel_args, $context = NULL) {
++ $block = new stdClass();
++
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : t('Legend');
++ $block->content = '' . t('Critical issue') . '
';
++ $block->content .= '' . t('Major issue') . '
';
++ $block->content .= '' . t('Current top priority') . '
';
++
++ return $block;
++}
+\ No newline at end of file
+diff --git a/plugins/content_types/rocketship_link.inc b/plugins/content_types/rocketship_link.inc
+new file mode 100644
+index 0000000..28d8dec
+--- /dev/null
++++ b/plugins/content_types/rocketship_link.inc
+@@ -0,0 +1,63 @@
++ TRUE,
++ 'title' => t('Rocketship Link'),
++ 'description' => t('Shows a link for certain tags to the drupal.org issue queue.'),
++ 'category' => t('Miscellaneous'),
++ 'edit form' => 'rocketship_link_form',
++ 'render callback' => 'rocketship_link_render',
++ 'admin info' => 'rocketship_link_admin_info',
++ 'required context' => new ctools_context_optional(t('Terms'), 'terms'),
++);
++
++/**
++ * Info callback for the Rocketship link panel.
++ */
++function rocketship_link_admin_info($subtype, $conf, $contexts) {
++ if (!empty($conf)) {
++ $block = new stdClass;
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
++ $block->content = t('Showing a link to the drupal issue queue.');
++ return $block;
++ }
++}
++
++/**
++ * Configuration form for the Rocketship link panel.
++ */
++function rocketship_link_form($form, &$form_state) {
++ return $form;
++}
++
++/**
++ * Renders the Rocketship link panel.
++ */
++function rocketship_link_render($subtype, $conf, $panel_args, $context = NULL) {
++ $operator = isset($context->operator) ? $context->operator : 'and';
++ $tags = array();
++ if (!empty($context->tids)) {
++ foreach ($context->tids as $tid) {
++ if (empty($tags[$tid])) {
++ $term = taxonomy_term_load($tid);
++ $tags[$tid] = $term->name;
++ }
++ }
++ }
++ $path = rocketship_get_issue_path($tags, $operator);
++
++ $block = new stdClass();
++
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
++
++ $block->content = t('See all these issues also at !link. This view of issues is automatically cached and might be out of date up to two hours at times.', array(
++ '!link' => l(url($path['path'], $path), $path['path'], $path),
++ ));
++
++ return $block;
++}
+diff --git a/plugins/content_types/rocketship_thanks.inc b/plugins/content_types/rocketship_thanks.inc
+new file mode 100644
+index 0000000..d80f72a
+--- /dev/null
++++ b/plugins/content_types/rocketship_thanks.inc
+@@ -0,0 +1,91 @@
++ TRUE,
++ 'title' => t('Rocketship Thanks'),
++ 'description' => t('Shows a tag cloud of the contributors.'),
++ 'category' => t('Miscellaneous'),
++ 'edit form' => 'rocketship_thanks_form',
++ 'render callback' => 'rocketship_thanks_render',
++ 'admin info' => 'rocketship_thanks_admin_info',
++);
++
++/**
++ * Info callback for the Rocketship legend panel.
++ */
++function rocketship_thanks_admin_info($subtype, $conf, $contexts) {
++ if (!empty($conf)) {
++ $block = new stdClass;
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : '';
++ $block->content = t('Tag cloud of the contributors.');
++ return $block;
++ }
++}
++
++/**
++ * Configuration form for the Rocketship legend panel.
++ */
++function rocketship_thanks_form($form, &$form_state) {
++ return $form;
++}
++
++/**
++ * Renders the Rocketship legend panel.
++ */
++function rocketship_thanks_render($subtype, $conf, $panel_args, $context = NULL) {
++ $block = new stdClass();
++
++ $block->title = $conf['override_title'] ? $conf['override_title_text'] : t('Thanks to');
++
++
++ // Include username cloud if the sprint tag is displayed.
++ $participant_stats = variable_get('rocketship_participant_stats', array());
++ if (!empty($participant_stats)) {
++ $lead_users = variable_get('rocketship_lead_user', array('Gábor Hojtsy'));
++ $participant_uids = variable_get('rocketship_participant_uids', array());
++ $participant_recent = variable_get('rocketship_participant_recent', array());
++
++ // Remove lead user and some users to avoid skewing the stats.
++ if (!empty($lead_users) && is_array($lead_users)) {
++ foreach ($lead_users as $lead_user) {
++ unset($participant_stats[$lead_user]);
++ }
++ }
++
++ unset($participant_stats['System Message']);
++ unset($participant_stats['Abandoned Projects']);
++
++ $usercloud = array();
++ $max = max(array_values($participant_stats));
++ $steps = array(1, 2, 3, 5, 7, 10);
++ foreach ($steps as $i => $step) {
++ foreach ($participant_stats as $name => $count) {
++ if (($step == 10) || ($count <= $step * ($max/10))) {
++ $class = 'rocketship-cloud-' . ($i+1);
++ if (in_array($participant_uids[$name], $participant_recent)) {
++ $class .= ' rocketship-cloud-recent';
++ }
++ $usercloud[$name] = '' . check_plain($name) . '';
++ unset($participant_stats[$name]);
++ }
++ }
++ }
++ // Sort by username in a case insensitive fashion.
++ uksort($usercloud, "strnatcasecmp");
++ $notes = array();
++ $notes[] = t('Based on participation in all @tag issues (font size) using a quasi-logarithmic scale and on recent activity (boldness).', array('@tag' => variable_get('rocketship_master_tag', 'D8MI')));
++ if (!empty($lead_users)) {
++ $notes[] = format_plural(count($lead_users), 'Excludes the initiative lead, @lead.', 'Excludes the initiative leads, @lead.', array('@lead' => implode(', ', $lead_users)));
++ }
++
++ $block->content = '' . join(' ', $usercloud) . '
';
++ $block->content .= '' . join(' ', $notes) . '
';
++ }
++
++ return $block;
++}
+\ No newline at end of file
+diff --git a/plugins/layouts/rocketship/rocketship.css b/plugins/layouts/rocketship/rocketship.css
+new file mode 100644
+index 0000000..426117b
+--- /dev/null
++++ b/plugins/layouts/rocketship/rocketship.css
+@@ -0,0 +1,143 @@
++.rocketship .panel-col-top,
++.rocketship .panel-col-middle,
++.rocketship .panel-col-bottom,
++.rocketship .panel-separator {
++ width: 100%;
++ clear: both;
++}
++
++.rocketship .panel-col-top .inside,
++.rocketship .panel-col-middle .inside {
++ margin-bottom: 1em;
++}
++
++.rocketship .panel-col-first {
++ float: left;
++ width: 33%;
++}
++
++.rocketship .panel-col-first .inside {
++ margin: 0 .5em 1em 0;
++}
++
++.rocketship .panel-col {
++ float: left;
++ width: 33%;
++}
++
++.rocketship .panel-col .inside {
++ margin: 0 .5em 1em .5em;
++}
++
++.rocketship .panel-col-last {
++ float: left;
++ width: 33%;
++}
++
++.rocketship .panel-col-last .inside {
++ margin: 0 0 1em .5em;
++}
++
++.rocketship .panel-separator {
++ margin: 0 0 1em 0;
++}
++
++.view-id-issues .views-row {
++ margin-bottom: 1em;
++}
++
++.rocketship-node,
++.panel-pane .view-id-issues .node-rocketship-issue {
++ padding: 0.2em 0.3em 0.2em 0.5em;
++ -moz-box-shadow: 0 0 0.5em rgba(0,0,0,0.4);
++ -webkit-box-shadow: 0 0 0.5em rgba(0,0,0,0.4);
++ border: none;
++ border-left: 0.7em solid #F5F5F5;
++}
++
++.panel-pane .view-id-issues .node-rocketship-issue h2 a {
++ font-weight: bold;
++ font-size: 0.8em;
++}
++
++.rocketship-focus,
++.panel-pane .view-id-issues .node-rocketship-issue-focus {
++ border-left-color: #336699;
++}
++
++.rocketship-major,
++.panel-pane .view-id-issues .node-rocketship-issue-major {
++ border-left-color: #FF9900;
++}
++
++.rocketship-critical,
++.panel-pane .view-id-issues .node-rocketship-issue-critical {
++ border-left-color: #FF0000;
++}
++
++.rocketship-needs-tests,
++.panel-pane .view-id-issues .node-rocketship-issue-needs-tests {
++ font-weight: bold;
++}
++
++.view-id-issues .node-rocketship-issue .field-type-taxonomy-term-reference li {
++ background-color: #F5F5F5;
++ margin: 0.1em 0.3em 0.1em 0;
++ padding: 0.1em 0.3em;
++ display: inline-block;
++ border-radius: 0.2em;
++}
++
++.pane-rocketship-legend {
++ float: right;
++ clear: both;
++}
++.pane-rocketship-legend .pane-title,
++.pane-rocketship-legend .pane-content,
++.pane-rocketship-legend .rocketship-node {
++ display: inline-block;
++ margin-left: 6px;
++}
++.panel-pane .view-id-issues .node-rocketship-issue .field-name-rocketship-assignee {
++ background-color: #F5F5F5;
++ margin: -0.2em -0.3em -0.3em -0.5em;
++ padding: 0.1em 1em;
++ border: none;
++ border-top: 0.1em dotted #DDDDDD;
++ font-size: 0.9em;
++}
++.rocketship-cloud-wrapper {
++ border-top: 0.1em solid #3b3b3b;
++ margin-bottom: 2em;
++ clear: both;
++}
++.rocketship-cloud-wrapper p {
++ font-size: 0.8em;
++ margin-top: 1.3em;
++ font-style: italic;
++}
++.rocketship-cloud {
++ line-height: 1.3em;
++ text-align: justify;
++}
++.rocketship-cloud-1 {
++ font-size: 0.8em;
++}
++.rocketship-cloud-2 {
++ font-size: 1em;
++}
++.rocketship-cloud-3 {
++ font-size: 1.4em;
++}
++.rocketship-cloud-4 {
++ font-size: 1.6em;
++}
++.rocketship-cloud-5 {
++ font-size: 1.8em;
++}
++.rocketship-cloud-6 {
++ font-size: 2em;
++}
++.rocketship-cloud-recent a {
++ font-weight: bold;
++}
+diff --git a/plugins/layouts/rocketship/rocketship.inc b/plugins/layouts/rocketship/rocketship.inc
+new file mode 100644
+index 0000000..412c329
+--- /dev/null
++++ b/plugins/layouts/rocketship/rocketship.inc
+@@ -0,0 +1,25 @@
++ t('Rocketship'),
++ 'category' => t('Columns: 3'),
++ 'icon' => 'rocketship.png',
++ 'theme' => 'rocketship',
++ 'css' => 'rocketship.css',
++ 'regions' => array(
++ 'top' => t('Top'),
++ 'upper_first' => t('Upper First'),
++ 'upper_middle' => t('Upper Middle'),
++ 'upper_last' => t('Upper Last'),
++ 'middle' => t('Middle'),
++ 'lower_first' => t('Lower First'),
++ 'lower_middle' => t('Lower Middle'),
++ 'lower_last' => t('Lower Last'),
++ 'bottom' => t('Bottom')
++ ),
++);
+diff --git a/plugins/layouts/rocketship/rocketship.tpl.php b/plugins/layouts/rocketship/rocketship.tpl.php
+new file mode 100644
+index 0000000..18b6fc2
+--- /dev/null
++++ b/plugins/layouts/rocketship/rocketship.tpl.php
+@@ -0,0 +1,64 @@
++
++>
++
++
++
++
++
++
++
++
++
++
+diff --git a/rocketship.info b/rocketship.info
+index d8933d5..bfb85f5 100644
+--- a/rocketship.info
++++ b/rocketship.info
+@@ -1,3 +1,10 @@
+ name = Rocketship
+ description = Issue augmentation for Drupal initiatives
+-core = 7.x
+\ No newline at end of file
++core = 7.x
++
++dependencies[] = ctools
++dependencies[] = page_manager
++dependencies[] = panels
++dependencies[] = taxonomy
++dependencies[] = views
++dependencies[] = views_content
+diff --git a/rocketship.install b/rocketship.install
+new file mode 100644
+index 0000000..2899d5b
+--- /dev/null
++++ b/rocketship.install
+@@ -0,0 +1,151 @@
++ 'Issue tags',
++ 'machine_name' => 'rocketship_tags',
++ 'description' => '',
++ 'hierarchy' => 0,
++ );
++ taxonomy_vocabulary_save($vocabulary);
++ }
++
++ $tags = array('patch (to be ported)', '6.x-dev', '7.x-dev', 'fixed',
++ 'closed (duplicate)', 'closed (won\'t fix)', 'closed (works as designed)',
++ 'closed (cannot reproduce)', 'closed (fixed)', 'postponed',
++ 'postponed (maintainer needs more info)', 'active', 'needs work',
++ 'reviewed & tested by the community', 'needs review',
++ );
++ foreach ($tags as $tag) {
++ if (!taxonomy_get_term_by_name($tag)) {
++ taxonomy_term_save((object) array(
++ 'vid' => $vocabulary->vid,
++ 'name' => $tag,
++ ));
++ }
++ }
++
++
++ if (!field_info_field('rocketship_assignee')) {
++ field_create_field(array(
++ 'field_name' => 'rocketship_assignee',
++ 'type' => 'text',
++ ));
++ }
++
++ if (!field_info_field('rocketship_priority')) {
++ field_create_field(array(
++ 'field_name' => 'rocketship_priority',
++ 'type' => 'number_integer',
++ ));
++ }
++
++ if (!field_info_field('rocketship_tags')) {
++ field_create_field(array(
++ 'field_name' => 'rocketship_tags',
++ 'type' => 'taxonomy_term_reference',
++ 'settings' => array(
++ 'allowed_values' => array(
++ array('vocabulary' => 'rocketship_tags', 'parent' => NULL),
++ ),
++ ),
++ 'cardinality' => -1,
++ ));
++ }
++
++ if (!field_info_instance('node', 'rocketship_tags', 'rocketship_issue')) {
++ field_create_instance(array(
++ 'label' => 'Tags',
++ 'field_name' => 'rocketship_tags',
++ 'entity_type' => 'node',
++ 'bundle' => 'rocketship_issue',
++ 'widget' => array('type' => 'taxonomy_autocomplete'),
++ 'display' => array(
++ 'default' => array(
++ 'label' => 'hidden',
++ 'type' => 'taxonomy_term_reference_plain',
++ 'weight' => 0,
++ ),
++ 'teaser' => array(
++ 'label' => 'hidden',
++ 'type' => 'taxonomy_term_reference_plain',
++ 'weight' => 0,
++ ),
++ ),
++ ));
++ }
++
++ if (!field_info_instance('node', 'rocketship_assignee', 'rocketship_issue')) {
++ field_create_instance(array(
++ 'label' => 'Assigned to',
++ 'field_name' => 'rocketship_assignee',
++ 'entity_type' => 'node',
++ 'bundle' => 'rocketship_issue',
++ 'widget' => array('type' => 'text_textfield'),
++ 'display' => array(
++ 'default' => array(
++ 'label' => 'inline',
++ 'type' => 'text_default',
++ 'weight' => 1,
++ ),
++ 'teaser' => array(
++ 'label' => 'inline',
++ 'type' => 'text_default',
++ 'weight' => 1,
++ ),
++ ),
++ ));
++ }
++
++ if (!field_info_instance('node', 'rocketship_priority', 'rocketship_issue')) {
++ field_create_instance(array(
++ 'label' => 'Priority',
++ 'field_name' => 'rocketship_priority',
++ 'entity_type' => 'node',
++ 'bundle' => 'rocketship_issue',
++ 'widget' => array(
++ 'type' => 'number',
++ ),
++ 'display' => array(
++ 'default' => array('type' => 'hidden'),
++ 'teaser' => array('type' => 'hidden'),
++ ),
++ ));
++ }
++
++ if (!field_info_instance('node', 'rocketship_tags', 'rocketship_focus')) {
++ field_create_instance(array(
++ 'label' => 'Tags',
++ 'field_name' => 'rocketship_tags',
++ 'entity_type' => 'node',
++ 'bundle' => 'rocketship_focus',
++ 'widget' => array('type' => 'taxonomy_autocomplete'),
++ ));
++ }
++}
++
++/**
++ * Implements hook_uninstall().
++ */
++function rocketship_uninstall() {
++ field_delete_field('rocketship_tags');
++ field_delete_field('rocketship_assignee');
++ field_delete_field('rocketship_priority');
++
++ $vocabulary = taxonomy_vocabulary_machine_name_load('rocketship_tags');
++ taxonomy_vocabulary_delete($vocabulary->vid);
++}
+\ No newline at end of file
+diff --git a/rocketship.module b/rocketship.module
+index 54d0c2d..4be2d33 100644
+--- a/rocketship.module
++++ b/rocketship.module
+@@ -1,22 +1,10 @@
+ array(
++ 'name' => t('Issue'),
++ 'base' => 'node_content',
++ 'description' => '',
++ 'has_title' => '1',
++ 'title_label' => t('Title'),
++ 'help' => '',
++ ),
++ 'rocketship_focus' => array(
++ 'name' => t('Issue Focus'),
++ 'base' => 'node_content',
++ 'description' => '',
++ 'has_title' => '1',
++ 'title_label' => t('Title'),
++ 'help' => '',
++ ),
++ );
++ return $items;
++}
++
++/**
++ * Implements hook_views_api().
++ */
++function rocketship_views_api() {
++ return array(
++ 'api' => 3,
++ 'path' => drupal_get_path('module', 'rocketship'),
++ );
++}
++
++/**
++ * Implementation of hook_ctools_plugin_api().
++ */
++function rocketship_ctools_plugin_api($module, $api) {
++ if ($module == 'page_manager' && $api == 'pages_default') {
++ return array('version' => 1);
++ }
++}
++
++/**
++ * Implementation of hook_ctools_plugin_directory()
++ */
++function rocketship_ctools_plugin_directory($owner, $plugin_type) {
++ $supported = array('content_types', 'layouts');
++ if (in_array($plugin_type, $supported)) {
++ return 'plugins/' . $plugin_type;
++ }
++}
++
++/**
+ * Implements hook_cron().
+ */
+ function rocketship_cron() {
+@@ -65,14 +107,16 @@ function rocketship_launch_form_submit() {
+ */
+ function rocketship_parse_all() {
+ // Grab the first page of the listings of issues for this tag.
+- $issue_page = 'http://drupal.org/project/issues/search/drupal?issue_tags=' . urlencode(variable_get('rocketship_master_tag', 'D8MI'));
+- $page_output = drupal_http_request($issue_page);
++ $tags = array(variable_get('rocketship_master_tag', 'D8MI'));
++ $path = rocketship_get_issue_path($tags);
++ $url = url($path['path'], $path);
++ $page_output = drupal_http_request($url);
+ if ($page_output->code == 200 && !empty($page_output->data)) {
+
+ // Figure out the index number of the last issue list page by scraping.
+ $last_page_num = 0;
+ if (preg_match('!href="([^"]+)" title="Go to last page"!', $page_output->data, $last_page)) {
+- if (preg_match('!\?page=(\d+)&!', $last_page[1], $last_page_find)) {
++ if (preg_match('!\?page=(\d+)&!', $last_page[1], $last_page_find)) {
+ $last_page_num = $last_page_find[1];
+ }
+ }
+@@ -83,7 +127,9 @@ function rocketship_parse_all() {
+ // If we have more pages, go on to the rest of the pages as well.
+ if (!empty($last_page_num)) {
+ for ($page_num = 1; $page_num <= $last_page_num; $page_num++) {
+- $page_output = drupal_http_request($issue_page . '&page='. $page_num);
++ $path['query']['page'] = $page_num;
++ $url = url($path['path'], $path);
++ $page_output = drupal_http_request($url);
+ if ($page_output->code == 200 && !empty($page_output->data)) {
+ rocketship_parse_issue_page($page_output->data);
+ }
+@@ -123,11 +169,10 @@ function rocketship_stat_add_username($username = NULL, $uid = NULL, $time = NUL
+ /**
+ * Data scraper for a drupal.org issue listing page.
+ *
+- * @todo
++ * @todo Drop outdated nodes and unused tags.
+ * Does not drop outdated nodes (if master tag is removed), only updates old
+ * ones and create new ones. Also does not drop unused tags.
+- * @todo
+- * Does not consider multi-page issues. Yeah.
++ * @todo Does not consider multi-page issues. Yeah.
+ */
+ function rocketship_parse_issue_page(&$issue_page_content) {
+
+@@ -146,7 +191,9 @@ function rocketship_parse_issue_page(&$issue_page_content) {
+ // pattern, where 12345 is the issue nid from drupal.org and "title" is
+ // the issue title from drupal.org. Since all that stays the same for
+ // a drupal.org is the issue number, we look for that specifically.
+- $nid = db_query("SELECT nid FROM {node} WHERE type = :type AND title LIKE '#" . (int) $match . ":%'", array(':type' => ROCKETSHIP_NODE_TYPE))->fetchField();
++ $nid = db_query("SELECT nid FROM {node} WHERE type = 'rocketship_issue' AND title LIKE :title", array(
++ ':title' => '#' . (int) $match . ':%'
++ ))->fetchField();
+ if ($nid) {
+ $node = node_load($nid);
+ }
+@@ -155,7 +202,7 @@ function rocketship_parse_issue_page(&$issue_page_content) {
+ // and other listings of nodes, taxonomy index data is only stored for
+ // published nodes, so this is what we can do to get easy results
+ // quickly.
+- $node = (object) array('type' => ROCKETSHIP_NODE_TYPE, 'language' => 'und', 'status' => 1);
++ $node = (object) array('type' => 'rocketship_issue', 'language' => 'und', 'status' => 1);
+ }
+
+ // Grab node title based on drupal.org HTML markup.
+@@ -175,9 +222,14 @@ function rocketship_parse_issue_page(&$issue_page_content) {
+ }
+ }
+
+- // Assumes a "field_assigned" text field is present on this node type.
+- if (preg_match('!Assigned: | (.*) | !', $issue_node->data, $assigned)) {
+- $node->field_assigned['und'][0]['value'] = strip_tags($assigned[1]);
++ if (preg_match('!Assigned: | (.*) | !', $issue_node->data, $match)) {
++ $assigned = strip_tags($match[1]);
++ if ($assigned != 'Unassigned') {
++ $node->rocketship_assingee['und'][0]['value'] = $assigned;
++ }
++ else {
++ $node->rocketship_assingee['und'] = array();
++ }
+ }
+
+ // Store all kinds of metadata as plain tags on this node. We can then
+@@ -206,29 +258,49 @@ function rocketship_parse_issue_page(&$issue_page_content) {
+
+ // Now try and find existing tags with matching names if present or
+ // create the new tags if needed.
+- $node->field_issue_tags['und'] = array();
++ $node->rocketship_tags['und'] = array();
++ $vocabulary = taxonomy_vocabulary_machine_name_load('rocketship_tags');
+ foreach($tags as $tag) {
+ // This is an ugly pattern but there might be multiple matching
+ // terms, so we need to find the one matching in our vocabulary.
+ $matching_terms = taxonomy_get_term_by_name($tag);
+ if (!empty($matching_terms)) {
+ foreach ($matching_terms as $term) {
+- if ($term->vid == variable_get('rocketship_tags_vid', 6)) {
+- $node->field_issue_tags['und'][] = array('tid' => $term->tid);
++ if ($term->vid == $vocabulary->vid) {
++ $node->rocketship_tags['und'][] = array('tid' => $term->tid);
+ }
+ }
+ }
+ else {
+ // Not found, so create a new tag and assign.
+ $term = (object) array(
+- 'vid' => variable_get('rocketship_tags_vid', 6),
++ 'vid' => $vocabulary->vid,
+ 'name' => $tag,
+ );
+ taxonomy_term_save($term);
+- $node->field_issue_tags['und'][] = array('tid' => $term->tid);
++ $node->rocketship_tags['und'][] = array('tid' => $term->tid);
++ }
++ }
++
++ $priority = 0;
++ foreach ($tags as $tag) {
++ switch ($tag) {
++ case 'Needs tests':
++ break;
++ case 'major':
++ $priority = min(-5, $priority);
++ break;
++ case 'critical':
++ $priority = min(-7, $priority);
++ break;
++ case 'sprint':
++ $priority = min(-10, $priority);
++ break;
+ }
+ }
+
++ $node->rocketship_priority['und'][0]['value'] = $priority;
++
+ // Save our updated / created node.
+ node_save($node);
+ }
+@@ -237,396 +309,59 @@ function rocketship_parse_issue_page(&$issue_page_content) {
+ }
+
+ /**
+- * Display controlled for nodes.
++ * Gets the drupal issue search path.
+ *
+- * This assumes nodes where we want to display a list of issues have a
+- * "field_issue_tag_display" single value taxonomy term reference field,
+- * which designates the tag that we want to dive into (from all the)
+- * issues that we gathered. This can be equal to the rocketship_master_tag
+- * variable if we want a list of all locally cached issues.
++ * @param array $tags
++ * List of tags to query for
+ *
+- * The primary goal here is to group the issues in sensible groups and
+- * display them by their group to help produce a visual overview.
++ * @return array
++ * url() compatible array structure.
+ */
+-function rocketship_node_view($node, $view_mode, $langcode) {
+- if (!empty($node->field_issue_tag_display['und'])) {
+-
+- // Get more info on the term we are presenting this list for.
+- $tid_for_list = $node->field_issue_tag_display['und'][0]['tid'];
+- $term_for_list = taxonomy_term_load($tid_for_list);
+-
+- // Get all the issue nodes that have the tag we need.
+- $nids = db_query('SELECT nid FROM {taxonomy_index} WHERE tid = :tid', array(':tid' => $tid_for_list))->fetchAll();
+-
+- // Look up a cached version for this term if we have it and
+- // it is not yet expired (no newer nodes of the issue type
+- // available yet).
+- if ($cache = cache_get('rocketship:' . $tid_for_list)) {
+- $last_issue_update = db_query("SELECT changed FROM {node} WHERE type = :type ORDER BY created DESC", array(':type' => ROCKETSHIP_NODE_TYPE))->fetchField();
+- if ($cache->created > $last_issue_update) {
+- $node->content['rocketship_issues'] = array(
+- '#markup' => $cache->data,
+- // Place at the end of the page before book navigation (if present).
+- '#weight' => 80,
+- );
+- return;
+- }
+- }
+-
+- // Now come some tricky wrangly of the data. We want to display
+- // two 3-column tables. The first one is the primary overview
+- // with the second more informal about issues we are not currently
+- // focusing on.
+- $tables = array(
+- 1 => t('Currently in the works for Drupal 8'),
+- 2 => t('Other related issues'),
+- );
+-
+- // The columns for these two tables are intermingled here to make
+- // term matching simpler without complicated conditions. We achieve
+- // categorization without complex conditions by prioritizing the
+- // placement of issues in categories with their tags.
+- $columns = array(
+- // Need this first so that we catch the backport patches first and then
+- // the rest can sort itself based on simpler tag lookups.
+- t('Backport') => array(array('patch (to be ported)', '6.x-dev', '7.x-dev'), 2),
+- t('To do') => array(array('active', 'needs work'), 1),
+- t('To review') => array(array('needs review'), 1),
+- t('To be committed') => array(array('reviewed & tested by the community'), 1),
+- t('Postponed') => array(array('postponed', 'postponed (maintainer needs more info)'), 2),
+- t('Closed') => array(array('fixed', 'closed (duplicate)', "closed (won't fix)", 'closed (works as designed)', 'closed (cannot reproduce)', 'closed (fixed)'), 2),
++function rocketship_get_issue_path(array $tags, $operator = 'and') {
++ $path = array();
++ $path['path'] = 'http://drupal.org/project/issues/search/' . variable_get('rocketship_project', 'drupal');
++
++ if (!empty($tags)) {
++ $path['query'] = array(
++ 'issue_tags' => implode(',', $tags),
++ 'issue_tags_op' => $operator,
+ );
++ }
+
+- // Some trickery to replace the term names in the $columns array
+- // with term ids for easier lookup later and collect a list of
+- // all those term IDs for later matching.
+- $status_tids = array();
+- foreach ($columns as $name => &$terms) {
+- foreach ($terms[0] as &$term_name) {
+- if ($matching_terms = taxonomy_get_term_by_name($term_name)) {
+- foreach ($matching_terms as $term) {
+- if ($term->vid == variable_get('rocketship_tags_vid', 6)) {
+- // Replace term name in $columns with term ID (the variable
+- // is taken by reference, so we directly modify the arary here).
+- $term_name = $term->tid;
+- // Collect a complete list of all status tids.
+- $status_tids[] = $term->tid;
+- break;
+- }
+- }
+- }
+- else {
+- // This might happen if we never had issues with the given status tag,
+- // so we don't have the status tag created yet. In this case, we should
+- // ignore this tag for now.
+- $term_name = 0;
+- }
+- }
+- }
+-
+- // Now load the nodes we found for the page tag and categorize into
+- // the groups defined by terms in $columns.
+- $nodes = array();
+- foreach($nids as $nid) {
+- $issue_node = node_load($nid->nid);
+- // Tags on the issue node should be referenced in field_issue_tags[].
+- if (!empty($issue_node->field_issue_tags['und'])) {
+- foreach ($issue_node->field_issue_tags['und'] as $tag_info) {
+- if (in_array($tag_info['tid'], $status_tids)) {
+- // If this was a status tid, look at which group it is in,
+- // in which case we found where to categorize the node.
+- foreach ($columns as $title => $status_map) {
+- if (in_array($tag_info['tid'], $status_map[0])) {
+- // Save the whole node object categorized under the $columns
+- // $title for this column and move two levels up to the next
+- // node.
+- $nodes[$title][] = $issue_node;
+- break 2;
+- }
+- }
+- }
+- }
+- }
+- }
+-
+- // Now sort the $nodes array into two tables and fill in the table
+- // headers and cells appropriately.
+- $header_data = array();
+- $table_data = array();
+- foreach ($columns as $header => $columns_data) {
+- // Sort nodes by "priority" defined by current focus as most important and
+- // then major and critical being slightly less important and then the rest.
+- $cell = array(-10 => '', -7 => '', -5 => '', 0 => '');
+- if (!empty($nodes[$header])) {
+- foreach ($nodes[$header] as $issue_node) {
+- $classes = 'rocketship-node';
+- $cell_terms = '';
+- $priority = 0;
+- foreach ($issue_node->field_issue_tags['und'] as $tag_info) {
+- $term = taxonomy_term_load($tag_info['tid']);
+- $term_class = 'rocketship-term';
+- switch($term->name) {
+- // Style the needs tests tag specially.
+- // @todo: this has some more potential, think about it.
+- case 'Needs tests':
+- $term_class .= ' rocketship-needs-tests';
+- break;
+- // Mark issues with major and critical status.
+- case 'major':
+- $classes .= ' rocketship-major';
+- $priority = min(-5, $priority);
+- break;
+- case 'critical':
+- $classes .= ' rocketship-critical';
+- $priority = min(-7, $priority);
+- break;
+- // Use the "sprint" tag to mark issues for current focus.
+- case 'sprint':
+- $classes .= ' rocketship-focus';
+- $priority = min(-10, $priority);
+- break;
+- }
+- // Remove tags that are "trivial" in this listing. The master tag
+- // is always trivial, the tool focuses on 8.x issues, so those
+- // not having that tag should be assumed to be 8.x, RTBC and
+- // needs review issues are in their respective column, so that is
+- // already represented, and the current page tag is presented on
+- // the page, so again no need to repeat it on all nodes (it is not
+- // a distinguishing factor).
+- if (!in_array($term->name, array(variable_get('rocketship_master_tag', 'D8MI'), 'reviewed & tested by the community', 'needs review', '8.x-dev', $term_for_list->name))) {
+- // Add up all terms with their respective tags.
+- $cell_terms .= ''. check_plain($term->name) .'';
+- }
+- }
+-
+- // Add rocketship-normal class for easier styling.
+- if ($priority == 0) {
+- $classes .= ' rocketship-normal';
+- }
+-
+- // Link to drupal.org node for this issue.
+- $drupalorg_nid = preg_replace('!#(\d+):(.*)$!', '\1', $issue_node->title);
+- $cell[$priority] .= ''. l($issue_node->title, 'http://drupal.org/node/'. $drupalorg_nid) .'
';
+- $cell[$priority] .= $cell_terms;
+- if (!empty($issue_node->field_assigned['und'][0]['value']) && $issue_node->field_assigned['und'][0]['value'] != 'Unassigned') {
+- $cell[$priority] .= '
' . t('Assigned to @assigned', array('@assigned' => $issue_node->field_assigned['und'][0]['value'])) . "
";
+- }
+- $cell[$priority] .= '
';
+- }
+- }
+-
+- // Store this cell and header in the right table. Each table will only
+- // have one cell per column (which might not look nice), but it gets us
+- // to the best display results. For purity's sake we might want to
+- // rebuild this based on a three column layout, that is what we
+- // accomplish with the tables basically.
+- $table_data[$columns_data[1]][] = join('', $cell);
+- $header_data[$columns_data[1]][] = $header;
+- }
++ return $path;
++}
+
+- // Styling and some usabiltiy improvement JS included inline. This is indeed
+- // very ugly but is quickly accessible for development and can be refactored
+- // later.
+- $output = <<
+-STYLE;
+-
+- // Produce a short legend to be included with each table.
+- $legend = 'Legend:
Critical issue
Major issue
Current top priority
';
+- foreach($tables as $num => $title) {
+- // Lead the table with a legend.
+- $output .= $legend . ''. $title .'
';
+- // Link to drupal.org issue list for the same issue overview. Reassures
+- // users that there is no special data showcased here, they can still
+- // access all this on drupal.org.
+- $issue_list_url = 'http://drupal.org/project/issues/search/drupal?issue_tags=' . urlencode($term_for_list->name) . ($term_for_list->name == variable_get('rocketship_master_tag', 'D8MI') ? '' : '%2C+' . variable_get('rocketship_master_tag', 'D8MI') . '&issue_tags_op=and');
+- $output .= 'See all these issues also at ' .check_plain($issue_list_url) . '. This view of issues is automatically cached and might be out of date up to two hours at times.
';
+-
+- // Theme the respective "table" with responsive markup.
+- $output .= '';
+- foreach ($header_data[$num] as $i => $cell) {
+- $output .= '
' . $cell . '
';
+- if (empty($table_data[$num][$i])) {
+- $output .= t('(None)');
+- }
+- else {
+- $output .= $table_data[$num][$i];
+- }
+- $output .= '';
+- }
+- $output .= '
';
+- // $output .= theme('table', array('header' => $header_data[$num], 'rows' => array($table_data[$num])));
+- }
+- $output .= '';
+-
+- // Include username cloud if the sprint tag is displayed.
+- $participant_stats = variable_get('rocketship_participant_stats', array());
+- if ($term_for_list->name == 'sprint' && !empty($participant_stats)) {
+- $lead_user = variable_get('rocketship_lead_user', 'Gábor Hojtsy');
+- $participant_uids = variable_get('rocketship_participant_uids', array());
+- $participant_recent = variable_get('rocketship_participant_recent', array());
+-
+- // Remove lead user and some users to avoid skewing the stats.
+- if ($lead_user) {
+- unset($participant_stats[$lead_user]);
+- }
+- unset($participant_stats['System Message']);
+- unset($participant_stats['Abandoned Projects']);
+-
+- $usercloud = array();
+- $max = max(array_values($participant_stats));
+- $steps = array(1, 2, 3, 5, 7, 10);
+- foreach ($steps as $i => $step) {
+- foreach ($participant_stats as $name => $count) {
+- if (($step == 10) || ($count <= $step * ($max/10))) {
+- $class = 'rocketship-cloud-' . ($i+1);
+- if (in_array($participant_uids[$name], $participant_recent)) {
+- $class .= ' rocketship-cloud-recent';
+- }
+- $usercloud[$name] = '' . check_plain($name) . '';
+- unset($participant_stats[$name]);
+- }
++/**
++ * Implements hook_preprocess_node().
++ */
++function rocketship_preprocess_node(&$variables) {
++ if ($variables['type'] == 'rocketship_issue' && !$variables['page']) {
++ $drupalorg_nid = preg_replace('!#(\d+):(.*)$!', '\1', $variables['title']);
++ $variables['node_url'] = 'http://drupal.org/node/' . $drupalorg_nid;
++
++ $rocketship_tags = field_get_items('node', $variables['node'], 'rocketship_tags');
++ if ($rocketship_tags) {
++ foreach ($rocketship_tags as $tag_info) {
++ $term = taxonomy_term_load($tag_info['tid']);
++ switch ($term->name) {
++ // Style the needs tests tag specially.
++ // @todo: this has some more potential, think about it.
++ case 'Needs tests':
++ $variables['classes_array'][] = 'node-rocketship-issue-needs-tests';
++ break;
++ // Mark issues with major and critical status.
++ case 'major':
++ $variables['classes_array'][] = 'node-rocketship-issue-major';
++ break;
++ case 'critical':
++ $variables['classes_array'][] = 'node-rocketship-issue-critical';
++ break;
++ // Use the "sprint" tag to mark issues for current focus.
++ case 'sprint':
++ $variables['classes_array'][] = 'node-rocketship-issue-focus';
++ break;
+ }
+ }
+- // Sort by username in a case insensitive fashion.
+- uksort($usercloud, "strnatcasecmp");
+- $note = '' . t('Based on participation in all @tag issues (font size) using a quasi-logarithmic scale and on recent activity (boldness).', array('@tag' => variable_get('rocketship_master_tag', 'D8MI')));
+- if ($lead_user) {
+- $note .= ' ' . t('Excludes the initiative lead, @lead.', array('@lead' => $lead_user));
+- }
+- $note .= '
';
+- $output .= '' . t('Thanks to') . ' #
' . '
' . join(' ', $usercloud) . '
' . $note . '
';
+ }
+-
+- // Save the generated page section in the rocketship cache.
+- cache_set('rocketship:' . $tid_for_list, $output);
+- watchdog('rocketship', 'Rocketship cache miss.');
+-
+- $node->content['rocketship_issues'] = array(
+- '#markup' => $output,
+- // Place at the end of the page before book navigation (if present).
+- '#weight' => 80,
+- );
+ }
+ }
+diff --git a/rocketship.pages_default.inc b/rocketship.pages_default.inc
+new file mode 100644
+index 0000000..449c582
+--- /dev/null
++++ b/rocketship.pages_default.inc
+@@ -0,0 +1,388 @@
++disabled = FALSE; /* Edit this to true to make a default handler disabled initially */
++ $handler->api_version = 1;
++ $handler->name = 'node_view_panel_context';
++ $handler->task = 'node_view';
++ $handler->subtask = '';
++ $handler->handler = 'panel_context';
++ $handler->weight = 0;
++ $handler->conf = array(
++ 'title' => 'Issues',
++ 'no_blocks' => 0,
++ 'pipeline' => 'standard',
++ 'body_classes_to_remove' => '',
++ 'body_classes_to_add' => '',
++ 'css_id' => '',
++ 'css' => '',
++ 'contexts' => array(),
++ 'relationships' => array(
++ 0 => array(
++ 'identifier' => 'Display Tags',
++ 'keyword' => 'tags',
++ 'name' => 'terms_from_node',
++ 'vocabulary' => array(
++ 'rocketship_tags' => 'rocketship_tags',
++ ),
++ 'concatenator' => ',',
++ 'context' => 'argument_entity_id:node_1',
++ 'id' => 1,
++ ),
++ ),
++ 'access' => array(
++ 'plugins' => array(
++ 0 => array(
++ 'name' => 'entity_bundle:node',
++ 'settings' => array(
++ 'type' => array(
++ 'rocketship_focus' => 'rocketship_focus',
++ ),
++ ),
++ 'context' => 'argument_entity_id:node_1',
++ 'not' => FALSE,
++ ),
++ ),
++ 'logic' => 'and',
++ ),
++ );
++ $display = new panels_display();
++ $display->layout = 'rocketship';
++ $display->layout_settings = array();
++ $display->panel_settings = array(
++ 'style_settings' => array(
++ 'default' => NULL,
++ 'middle' => NULL,
++ 'left' => NULL,
++ 'right' => NULL,
++ 'top' => NULL,
++ 'bottom' => NULL,
++ 'upper_first' => NULL,
++ 'upper_middle' => NULL,
++ 'upper_last' => NULL,
++ 'lower_first' => NULL,
++ 'lower_middle' => NULL,
++ 'lower_last' => NULL,
++ ),
++ );
++ $display->cache = array();
++ $display->title = '%node:title';
++ $display->content = array();
++ $display->panels = array();
++ $pane = new stdClass();
++ $pane->pid = 'new-1';
++ $pane->panel = 'bottom';
++ $pane->type = 'rocketship_thanks';
++ $pane->subtype = 'rocketship_thanks';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'override_title' => 0,
++ 'override_title_text' => '',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-1'] = $pane;
++ $display->panels['bottom'][0] = 'new-1';
++ $pane = new stdClass();
++ $pane->pid = 'new-2';
++ $pane->panel = 'lower_first';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'patch (to be ported), 6.x-dev, 7.x-dev',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'Backport',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-2'] = $pane;
++ $display->panels['lower_first'][0] = 'new-2';
++ $pane = new stdClass();
++ $pane->pid = 'new-3';
++ $pane->panel = 'lower_last';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'fixed, closed (duplicate), closed (won\'t fix), closed (works as designed), closed (cannot reproduce), closed (fixed)',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'Closed',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-3'] = $pane;
++ $display->panels['lower_last'][0] = 'new-3';
++ $pane = new stdClass();
++ $pane->pid = 'new-4';
++ $pane->panel = 'lower_middle';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'postponed, postponed (maintainer needs more info)',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'Postponed',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-4'] = $pane;
++ $display->panels['lower_middle'][0] = 'new-4';
++ $pane = new stdClass();
++ $pane->pid = 'new-5';
++ $pane->panel = 'middle';
++ $pane->type = 'rocketship_legend';
++ $pane->subtype = 'rocketship_legend';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'override_title' => 0,
++ 'override_title_text' => '',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-5'] = $pane;
++ $display->panels['middle'][0] = 'new-5';
++ $pane = new stdClass();
++ $pane->pid = 'new-6';
++ $pane->panel = 'middle';
++ $pane->type = 'rocketship_link';
++ $pane->subtype = 'rocketship_link';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'override_title' => 1,
++ 'override_title_text' => 'Other related issues',
++ 'context' => 'empty',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 1;
++ $pane->locks = array();
++ $display->content['new-6'] = $pane;
++ $display->panels['middle'][1] = 'new-6';
++ $pane = new stdClass();
++ $pane->pid = 'new-7';
++ $pane->panel = 'top';
++ $pane->type = 'node_content';
++ $pane->subtype = 'node_content';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'links' => 1,
++ 'no_extras' => 1,
++ 'override_title' => 0,
++ 'override_title_text' => '',
++ 'identifier' => '',
++ 'link' => 0,
++ 'leave_node_title' => 0,
++ 'build_mode' => 'full',
++ 'context' => 'argument_entity_id:node_1',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-7'] = $pane;
++ $display->panels['top'][0] = 'new-7';
++ $pane = new stdClass();
++ $pane->pid = 'new-8';
++ $pane->panel = 'top';
++ $pane->type = 'rocketship_legend';
++ $pane->subtype = 'rocketship_legend';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'override_title' => 0,
++ 'override_title_text' => '',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 1;
++ $pane->locks = array();
++ $display->content['new-8'] = $pane;
++ $display->panels['top'][1] = 'new-8';
++ $pane = new stdClass();
++ $pane->pid = 'new-9';
++ $pane->panel = 'top';
++ $pane->type = 'rocketship_link';
++ $pane->subtype = 'rocketship_link';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'override_title' => 1,
++ 'override_title_text' => 'Currently in the works for Drupal 8',
++ 'context' => 'relationship_terms_from_node_1',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 2;
++ $pane->locks = array();
++ $display->content['new-9'] = $pane;
++ $display->panels['top'][2] = 'new-9';
++ $pane = new stdClass();
++ $pane->pid = 'new-10';
++ $pane->panel = 'upper_first';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'active, needs work',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'To do',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-10'] = $pane;
++ $display->panels['upper_first'][0] = 'new-10';
++ $pane = new stdClass();
++ $pane->pid = 'new-11';
++ $pane->panel = 'upper_last';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'reviewed & tested by the community',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'To be committed',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-11'] = $pane;
++ $display->panels['upper_last'][0] = 'new-11';
++ $pane = new stdClass();
++ $pane->pid = 'new-12';
++ $pane->panel = 'upper_middle';
++ $pane->type = 'views_panes';
++ $pane->subtype = 'issues-focus';
++ $pane->shown = TRUE;
++ $pane->access = array();
++ $pane->configuration = array(
++ 'exposed' => array(
++ 'rocketship_tags_tid' => 'needs review',
++ ),
++ 'context' => array(
++ 0 => 'relationship_terms_from_node_1',
++ ),
++ 'override_title' => 1,
++ 'override_title_text' => 'To review',
++ );
++ $pane->cache = array();
++ $pane->style = array(
++ 'settings' => NULL,
++ 'style' => 'default',
++ );
++ $pane->css = array();
++ $pane->extras = array();
++ $pane->position = 0;
++ $pane->locks = array();
++ $display->content['new-12'] = $pane;
++ $display->panels['upper_middle'][0] = 'new-12';
++ $display->hide_title = PANELS_TITLE_NONE;
++ $display->title_pane = '0';
++ $handler->conf['display'] = $display;
++
++ $export['node_view_panel_context'] = $handler;
++
++ return $export;
++}
+diff --git a/rocketship.views_default.inc b/rocketship.views_default.inc
+new file mode 100644
+index 0000000..08be7cc
+--- /dev/null
++++ b/rocketship.views_default.inc
+@@ -0,0 +1,132 @@
++name = 'issues';
++ $view->description = '';
++ $view->tag = 'Rocketship';
++ $view->base_table = 'node';
++ $view->human_name = 'Issues';
++ $view->core = 7;
++ $view->api_version = '3.0';
++ $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
++
++ /* Display: Master */
++ $handler = $view->new_display('default', 'Master', 'default');
++ $handler->display->display_options['use_more_always'] = FALSE;
++ $handler->display->display_options['access']['type'] = 'perm';
++ $handler->display->display_options['cache']['type'] = 'none';
++ $handler->display->display_options['query']['type'] = 'views_query';
++ $handler->display->display_options['exposed_form']['type'] = 'basic';
++ $handler->display->display_options['pager']['type'] = 'none';
++ $handler->display->display_options['pager']['options']['offset'] = '0';
++ $handler->display->display_options['style_plugin'] = 'default';
++ $handler->display->display_options['row_plugin'] = 'node';
++ $handler->display->display_options['row_options']['links'] = FALSE;
++ /* No results behavior: Global: Text area */
++ $handler->display->display_options['empty']['area']['id'] = 'area';
++ $handler->display->display_options['empty']['area']['table'] = 'views';
++ $handler->display->display_options['empty']['area']['field'] = 'area';
++ $handler->display->display_options['empty']['area']['empty'] = TRUE;
++ $handler->display->display_options['empty']['area']['content'] = '(None)';
++ $handler->display->display_options['empty']['area']['format'] = 'plain_text';
++ /* Field: Content: Title */
++ $handler->display->display_options['fields']['title']['id'] = 'title';
++ $handler->display->display_options['fields']['title']['table'] = 'node';
++ $handler->display->display_options['fields']['title']['field'] = 'title';
++ $handler->display->display_options['fields']['title']['label'] = '';
++ $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
++ $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
++ /* Sort criterion: Content: Priority (rocketship_priority) */
++ $handler->display->display_options['sorts']['rocketship_priority_value']['id'] = 'rocketship_priority_value';
++ $handler->display->display_options['sorts']['rocketship_priority_value']['table'] = 'field_data_rocketship_priority';
++ $handler->display->display_options['sorts']['rocketship_priority_value']['field'] = 'rocketship_priority_value';
++ /* Sort criterion: Content: Updated date */
++ $handler->display->display_options['sorts']['changed']['id'] = 'changed';
++ $handler->display->display_options['sorts']['changed']['table'] = 'node';
++ $handler->display->display_options['sorts']['changed']['field'] = 'changed';
++ $handler->display->display_options['sorts']['changed']['order'] = 'DESC';
++ /* Contextual filter: Content: Has taxonomy term ID */
++ $handler->display->display_options['arguments']['tid']['id'] = 'tid';
++ $handler->display->display_options['arguments']['tid']['table'] = 'taxonomy_index';
++ $handler->display->display_options['arguments']['tid']['field'] = 'tid';
++ $handler->display->display_options['arguments']['tid']['default_action'] = 'empty';
++ $handler->display->display_options['arguments']['tid']['default_argument_type'] = 'fixed';
++ $handler->display->display_options['arguments']['tid']['summary']['number_of_records'] = '0';
++ $handler->display->display_options['arguments']['tid']['summary']['format'] = 'default_summary';
++ $handler->display->display_options['arguments']['tid']['summary_options']['items_per_page'] = '25';
++ $handler->display->display_options['arguments']['tid']['specify_validation'] = TRUE;
++ $handler->display->display_options['arguments']['tid']['validate']['type'] = 'taxonomy_term';
++ $handler->display->display_options['arguments']['tid']['validate_options']['vocabularies'] = array(
++ 'rocketship_tags' => 'rocketship_tags',
++ );
++ $handler->display->display_options['arguments']['tid']['validate_options']['type'] = 'tids';
++ $handler->display->display_options['arguments']['tid']['validate']['fail'] = 'empty';
++ $handler->display->display_options['arguments']['tid']['break_phrase'] = TRUE;
++ $handler->display->display_options['filter_groups']['operator'] = 'OR';
++ /* Filter criterion: Content: Priority (rocketship_priority) */
++ $handler->display->display_options['filters']['rocketship_priority_value']['id'] = 'rocketship_priority_value';
++ $handler->display->display_options['filters']['rocketship_priority_value']['table'] = 'field_data_rocketship_priority';
++ $handler->display->display_options['filters']['rocketship_priority_value']['field'] = 'rocketship_priority_value';
++ $handler->display->display_options['filters']['rocketship_priority_value']['operator'] = '<';
++ $handler->display->display_options['filters']['rocketship_priority_value']['value']['value'] = '0';
++ $handler->display->display_options['filters']['rocketship_priority_value']['group'] = 1;
++ /* Filter criterion: Content: Type */
++ $handler->display->display_options['filters']['type']['id'] = 'type';
++ $handler->display->display_options['filters']['type']['table'] = 'node';
++ $handler->display->display_options['filters']['type']['field'] = 'type';
++ $handler->display->display_options['filters']['type']['value'] = array(
++ 'rocketship_issue' => 'rocketship_issue',
++ );
++ $handler->display->display_options['filters']['type']['group'] = 1;
++ /* Filter criterion: Content: Published */
++ $handler->display->display_options['filters']['status']['id'] = 'status';
++ $handler->display->display_options['filters']['status']['table'] = 'node';
++ $handler->display->display_options['filters']['status']['field'] = 'status';
++ $handler->display->display_options['filters']['status']['value'] = 1;
++ $handler->display->display_options['filters']['status']['group'] = 1;
++ $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
++ /* Filter criterion: Content: Tags (rocketship_tags) */
++ $handler->display->display_options['filters']['rocketship_tags_tid']['id'] = 'rocketship_tags_tid';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['table'] = 'field_data_rocketship_tags';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['field'] = 'rocketship_tags_tid';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['value'] = '';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['group'] = 1;
++ $handler->display->display_options['filters']['rocketship_tags_tid']['exposed'] = TRUE;
++ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['operator_id'] = 'rocketship_tags_tid_op';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['label'] = 'Tags';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['operator'] = 'rocketship_tags_tid_op';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['identifier'] = 'rocketship_tags_tid';
++ $handler->display->display_options['filters']['rocketship_tags_tid']['vocabulary'] = 'rocketship_tags';
++
++ /* Display: Focus */
++ $handler = $view->new_display('panel_pane', 'Focus', 'focus');
++ $handler->display->display_options['pane_title'] = 'Taged Issues';
++ $handler->display->display_options['allow']['use_pager'] = 0;
++ $handler->display->display_options['allow']['items_per_page'] = 0;
++ $handler->display->display_options['allow']['offset'] = 0;
++ $handler->display->display_options['allow']['link_to_view'] = 0;
++ $handler->display->display_options['allow']['more_link'] = 0;
++ $handler->display->display_options['allow']['path_override'] = 0;
++ $handler->display->display_options['allow']['title_override'] = 'title_override';
++ $handler->display->display_options['allow']['exposed_form'] = 'exposed_form';
++ $handler->display->display_options['allow']['fields_override'] = 0;
++ $handler->display->display_options['argument_input'] = array(
++ 'tid' => array(
++ 'type' => 'context',
++ 'context' => 'terms.tids',
++ 'context_optional' => 0,
++ 'panel' => '0',
++ 'fixed' => '',
++ 'label' => 'Taxonomy term: Term ID',
++ ),
++ );
++ $handler->display->display_options['inherit_panels_path'] = '1';
++
++ $views[$view->name] = $view;
++ return $views;
++}
diff --git a/rocketship.info b/rocketship.info
index d8933d5..bfb85f5 100644
--- a/rocketship.info
+++ b/rocketship.info
@@ -1,3 +1,10 @@
name = Rocketship
description = Issue augmentation for Drupal initiatives
-core = 7.x
\ No newline at end of file
+core = 7.x
+
+dependencies[] = ctools
+dependencies[] = page_manager
+dependencies[] = panels
+dependencies[] = taxonomy
+dependencies[] = views
+dependencies[] = views_content
diff --git a/rocketship.install b/rocketship.install
new file mode 100644
index 0000000..68e2668
--- /dev/null
+++ b/rocketship.install
@@ -0,0 +1,152 @@
+ 'Issue tags',
+ 'machine_name' => 'rocketship_tags',
+ 'description' => '',
+ 'hierarchy' => 0,
+ );
+ taxonomy_vocabulary_save($vocabulary);
+ }
+ variable_set('rocketship_tags_vid', $vocabulary->vid);
+
+ $tags = array('patch (to be ported)', '6.x-dev', '7.x-dev', 'fixed',
+ 'closed (duplicate)', 'closed (won\'t fix)', 'closed (works as designed)',
+ 'closed (cannot reproduce)', 'closed (fixed)', 'postponed',
+ 'postponed (maintainer needs more info)', 'active', 'needs work',
+ 'reviewed & tested by the community', 'needs review',
+ );
+ foreach ($tags as $tag) {
+ if (!taxonomy_get_term_by_name($tag)) {
+ taxonomy_term_save((object) array(
+ 'vid' => $vocabulary->vid,
+ 'name' => $tag,
+ ));
+ }
+ }
+
+
+ if (!field_info_field('rocketship_assignee')) {
+ field_create_field(array(
+ 'field_name' => 'rocketship_assignee',
+ 'type' => 'text',
+ ));
+ }
+
+ if (!field_info_field('rocketship_priority')) {
+ field_create_field(array(
+ 'field_name' => 'rocketship_priority',
+ 'type' => 'number_integer',
+ ));
+ }
+
+ if (!field_info_field('rocketship_tags')) {
+ field_create_field(array(
+ 'field_name' => 'rocketship_tags',
+ 'type' => 'taxonomy_term_reference',
+ 'settings' => array(
+ 'allowed_values' => array(
+ array('vocabulary' => 'rocketship_tags', 'parent' => NULL),
+ ),
+ ),
+ 'cardinality' => -1,
+ ));
+ }
+
+ if (!field_info_instance('node', 'rocketship_tags', 'rocketship_issue')) {
+ field_create_instance(array(
+ 'label' => 'Tags',
+ 'field_name' => 'rocketship_tags',
+ 'entity_type' => 'node',
+ 'bundle' => 'rocketship_issue',
+ 'widget' => array('type' => 'taxonomy_autocomplete'),
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'type' => 'taxonomy_term_reference_plain',
+ 'weight' => 0,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'type' => 'taxonomy_term_reference_plain',
+ 'weight' => 0,
+ ),
+ ),
+ ));
+ }
+
+ if (!field_info_instance('node', 'rocketship_assignee', 'rocketship_issue')) {
+ field_create_instance(array(
+ 'label' => 'Assigned to',
+ 'field_name' => 'rocketship_assignee',
+ 'entity_type' => 'node',
+ 'bundle' => 'rocketship_issue',
+ 'widget' => array('type' => 'text_textfield'),
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'inline',
+ 'type' => 'text_default',
+ 'weight' => 1,
+ ),
+ 'teaser' => array(
+ 'label' => 'inline',
+ 'type' => 'text_default',
+ 'weight' => 1,
+ ),
+ ),
+ ));
+ }
+
+ if (!field_info_instance('node', 'rocketship_priority', 'rocketship_issue')) {
+ field_create_instance(array(
+ 'label' => 'Priority',
+ 'field_name' => 'rocketship_priority',
+ 'entity_type' => 'node',
+ 'bundle' => 'rocketship_issue',
+ 'widget' => array(
+ 'type' => 'number',
+ ),
+ 'display' => array(
+ 'default' => array('type' => 'hidden'),
+ 'teaser' => array('type' => 'hidden'),
+ ),
+ ));
+ }
+
+ if (!field_info_instance('node', 'rocketship_tags', 'rocketship_focus')) {
+ field_create_instance(array(
+ 'label' => 'Tags',
+ 'field_name' => 'rocketship_tags',
+ 'entity_type' => 'node',
+ 'bundle' => 'rocketship_focus',
+ 'widget' => array('type' => 'taxonomy_autocomplete'),
+ ));
+ }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function rocketship_uninstall() {
+ field_delete_field('rocketship_tags');
+ field_delete_field('rocketship_assignee');
+ field_delete_field('rocketship_priority');
+
+ $vocabulary = taxonomy_vocabulary_machine_name_load('rocketship_tags');
+ taxonomy_vocabulary_delete($vocabulary->vid);
+}
\ No newline at end of file
diff --git a/rocketship.module b/rocketship.module
index 54d0c2d..4be2d33 100644
--- a/rocketship.module
+++ b/rocketship.module
@@ -1,22 +1,10 @@
array(
+ 'name' => t('Issue'),
+ 'base' => 'node_content',
+ 'description' => '',
+ 'has_title' => '1',
+ 'title_label' => t('Title'),
+ 'help' => '',
+ ),
+ 'rocketship_focus' => array(
+ 'name' => t('Issue Focus'),
+ 'base' => 'node_content',
+ 'description' => '',
+ 'has_title' => '1',
+ 'title_label' => t('Title'),
+ 'help' => '',
+ ),
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function rocketship_views_api() {
+ return array(
+ 'api' => 3,
+ 'path' => drupal_get_path('module', 'rocketship'),
+ );
+}
+
+/**
+ * Implementation of hook_ctools_plugin_api().
+ */
+function rocketship_ctools_plugin_api($module, $api) {
+ if ($module == 'page_manager' && $api == 'pages_default') {
+ return array('version' => 1);
+ }
+}
+
+/**
+ * Implementation of hook_ctools_plugin_directory()
+ */
+function rocketship_ctools_plugin_directory($owner, $plugin_type) {
+ $supported = array('content_types', 'layouts');
+ if (in_array($plugin_type, $supported)) {
+ return 'plugins/' . $plugin_type;
+ }
+}
+
+/**
* Implements hook_cron().
*/
function rocketship_cron() {
@@ -65,14 +107,16 @@ function rocketship_launch_form_submit() {
*/
function rocketship_parse_all() {
// Grab the first page of the listings of issues for this tag.
- $issue_page = 'http://drupal.org/project/issues/search/drupal?issue_tags=' . urlencode(variable_get('rocketship_master_tag', 'D8MI'));
- $page_output = drupal_http_request($issue_page);
+ $tags = array(variable_get('rocketship_master_tag', 'D8MI'));
+ $path = rocketship_get_issue_path($tags);
+ $url = url($path['path'], $path);
+ $page_output = drupal_http_request($url);
if ($page_output->code == 200 && !empty($page_output->data)) {
// Figure out the index number of the last issue list page by scraping.
$last_page_num = 0;
if (preg_match('!href="([^"]+)" title="Go to last page"!', $page_output->data, $last_page)) {
- if (preg_match('!\?page=(\d+)&!', $last_page[1], $last_page_find)) {
+ if (preg_match('!\?page=(\d+)&!', $last_page[1], $last_page_find)) {
$last_page_num = $last_page_find[1];
}
}
@@ -83,7 +127,9 @@ function rocketship_parse_all() {
// If we have more pages, go on to the rest of the pages as well.
if (!empty($last_page_num)) {
for ($page_num = 1; $page_num <= $last_page_num; $page_num++) {
- $page_output = drupal_http_request($issue_page . '&page='. $page_num);
+ $path['query']['page'] = $page_num;
+ $url = url($path['path'], $path);
+ $page_output = drupal_http_request($url);
if ($page_output->code == 200 && !empty($page_output->data)) {
rocketship_parse_issue_page($page_output->data);
}
@@ -123,11 +169,10 @@ function rocketship_stat_add_username($username = NULL, $uid = NULL, $time = NUL
/**
* Data scraper for a drupal.org issue listing page.
*
- * @todo
+ * @todo Drop outdated nodes and unused tags.
* Does not drop outdated nodes (if master tag is removed), only updates old
* ones and create new ones. Also does not drop unused tags.
- * @todo
- * Does not consider multi-page issues. Yeah.
+ * @todo Does not consider multi-page issues. Yeah.
*/
function rocketship_parse_issue_page(&$issue_page_content) {
@@ -146,7 +191,9 @@ function rocketship_parse_issue_page(&$issue_page_content) {
// pattern, where 12345 is the issue nid from drupal.org and "title" is
// the issue title from drupal.org. Since all that stays the same for
// a drupal.org is the issue number, we look for that specifically.
- $nid = db_query("SELECT nid FROM {node} WHERE type = :type AND title LIKE '#" . (int) $match . ":%'", array(':type' => ROCKETSHIP_NODE_TYPE))->fetchField();
+ $nid = db_query("SELECT nid FROM {node} WHERE type = 'rocketship_issue' AND title LIKE :title", array(
+ ':title' => '#' . (int) $match . ':%'
+ ))->fetchField();
if ($nid) {
$node = node_load($nid);
}
@@ -155,7 +202,7 @@ function rocketship_parse_issue_page(&$issue_page_content) {
// and other listings of nodes, taxonomy index data is only stored for
// published nodes, so this is what we can do to get easy results
// quickly.
- $node = (object) array('type' => ROCKETSHIP_NODE_TYPE, 'language' => 'und', 'status' => 1);
+ $node = (object) array('type' => 'rocketship_issue', 'language' => 'und', 'status' => 1);
}
// Grab node title based on drupal.org HTML markup.
@@ -175,9 +222,14 @@ function rocketship_parse_issue_page(&$issue_page_content) {
}
}
- // Assumes a "field_assigned" text field is present on this node type.
- if (preg_match('!Assigned: | (.*) | !', $issue_node->data, $assigned)) {
- $node->field_assigned['und'][0]['value'] = strip_tags($assigned[1]);
+ if (preg_match('!Assigned: | (.*) | !', $issue_node->data, $match)) {
+ $assigned = strip_tags($match[1]);
+ if ($assigned != 'Unassigned') {
+ $node->rocketship_assingee['und'][0]['value'] = $assigned;
+ }
+ else {
+ $node->rocketship_assingee['und'] = array();
+ }
}
// Store all kinds of metadata as plain tags on this node. We can then
@@ -206,29 +258,49 @@ function rocketship_parse_issue_page(&$issue_page_content) {
// Now try and find existing tags with matching names if present or
// create the new tags if needed.
- $node->field_issue_tags['und'] = array();
+ $node->rocketship_tags['und'] = array();
+ $vocabulary = taxonomy_vocabulary_machine_name_load('rocketship_tags');
foreach($tags as $tag) {
// This is an ugly pattern but there might be multiple matching
// terms, so we need to find the one matching in our vocabulary.
$matching_terms = taxonomy_get_term_by_name($tag);
if (!empty($matching_terms)) {
foreach ($matching_terms as $term) {
- if ($term->vid == variable_get('rocketship_tags_vid', 6)) {
- $node->field_issue_tags['und'][] = array('tid' => $term->tid);
+ if ($term->vid == $vocabulary->vid) {
+ $node->rocketship_tags['und'][] = array('tid' => $term->tid);
}
}
}
else {
// Not found, so create a new tag and assign.
$term = (object) array(
- 'vid' => variable_get('rocketship_tags_vid', 6),
+ 'vid' => $vocabulary->vid,
'name' => $tag,
);
taxonomy_term_save($term);
- $node->field_issue_tags['und'][] = array('tid' => $term->tid);
+ $node->rocketship_tags['und'][] = array('tid' => $term->tid);
+ }
+ }
+
+ $priority = 0;
+ foreach ($tags as $tag) {
+ switch ($tag) {
+ case 'Needs tests':
+ break;
+ case 'major':
+ $priority = min(-5, $priority);
+ break;
+ case 'critical':
+ $priority = min(-7, $priority);
+ break;
+ case 'sprint':
+ $priority = min(-10, $priority);
+ break;
}
}
+ $node->rocketship_priority['und'][0]['value'] = $priority;
+
// Save our updated / created node.
node_save($node);
}
@@ -237,396 +309,59 @@ function rocketship_parse_issue_page(&$issue_page_content) {
}
/**
- * Display controlled for nodes.
+ * Gets the drupal issue search path.
*
- * This assumes nodes where we want to display a list of issues have a
- * "field_issue_tag_display" single value taxonomy term reference field,
- * which designates the tag that we want to dive into (from all the)
- * issues that we gathered. This can be equal to the rocketship_master_tag
- * variable if we want a list of all locally cached issues.
+ * @param array $tags
+ * List of tags to query for
*
- * The primary goal here is to group the issues in sensible groups and
- * display them by their group to help produce a visual overview.
+ * @return array
+ * url() compatible array structure.
*/
-function rocketship_node_view($node, $view_mode, $langcode) {
- if (!empty($node->field_issue_tag_display['und'])) {
-
- // Get more info on the term we are presenting this list for.
- $tid_for_list = $node->field_issue_tag_display['und'][0]['tid'];
- $term_for_list = taxonomy_term_load($tid_for_list);
-
- // Get all the issue nodes that have the tag we need.
- $nids = db_query('SELECT nid FROM {taxonomy_index} WHERE tid = :tid', array(':tid' => $tid_for_list))->fetchAll();
-
- // Look up a cached version for this term if we have it and
- // it is not yet expired (no newer nodes of the issue type
- // available yet).
- if ($cache = cache_get('rocketship:' . $tid_for_list)) {
- $last_issue_update = db_query("SELECT changed FROM {node} WHERE type = :type ORDER BY created DESC", array(':type' => ROCKETSHIP_NODE_TYPE))->fetchField();
- if ($cache->created > $last_issue_update) {
- $node->content['rocketship_issues'] = array(
- '#markup' => $cache->data,
- // Place at the end of the page before book navigation (if present).
- '#weight' => 80,
- );
- return;
- }
- }
-
- // Now come some tricky wrangly of the data. We want to display
- // two 3-column tables. The first one is the primary overview
- // with the second more informal about issues we are not currently
- // focusing on.
- $tables = array(
- 1 => t('Currently in the works for Drupal 8'),
- 2 => t('Other related issues'),
- );
-
- // The columns for these two tables are intermingled here to make
- // term matching simpler without complicated conditions. We achieve
- // categorization without complex conditions by prioritizing the
- // placement of issues in categories with their tags.
- $columns = array(
- // Need this first so that we catch the backport patches first and then
- // the rest can sort itself based on simpler tag lookups.
- t('Backport') => array(array('patch (to be ported)', '6.x-dev', '7.x-dev'), 2),
- t('To do') => array(array('active', 'needs work'), 1),
- t('To review') => array(array('needs review'), 1),
- t('To be committed') => array(array('reviewed & tested by the community'), 1),
- t('Postponed') => array(array('postponed', 'postponed (maintainer needs more info)'), 2),
- t('Closed') => array(array('fixed', 'closed (duplicate)', "closed (won't fix)", 'closed (works as designed)', 'closed (cannot reproduce)', 'closed (fixed)'), 2),
+function rocketship_get_issue_path(array $tags, $operator = 'and') {
+ $path = array();
+ $path['path'] = 'http://drupal.org/project/issues/search/' . variable_get('rocketship_project', 'drupal');
+
+ if (!empty($tags)) {
+ $path['query'] = array(
+ 'issue_tags' => implode(',', $tags),
+ 'issue_tags_op' => $operator,
);
+ }
- // Some trickery to replace the term names in the $columns array
- // with term ids for easier lookup later and collect a list of
- // all those term IDs for later matching.
- $status_tids = array();
- foreach ($columns as $name => &$terms) {
- foreach ($terms[0] as &$term_name) {
- if ($matching_terms = taxonomy_get_term_by_name($term_name)) {
- foreach ($matching_terms as $term) {
- if ($term->vid == variable_get('rocketship_tags_vid', 6)) {
- // Replace term name in $columns with term ID (the variable
- // is taken by reference, so we directly modify the arary here).
- $term_name = $term->tid;
- // Collect a complete list of all status tids.
- $status_tids[] = $term->tid;
- break;
- }
- }
- }
- else {
- // This might happen if we never had issues with the given status tag,
- // so we don't have the status tag created yet. In this case, we should
- // ignore this tag for now.
- $term_name = 0;
- }
- }
- }
-
- // Now load the nodes we found for the page tag and categorize into
- // the groups defined by terms in $columns.
- $nodes = array();
- foreach($nids as $nid) {
- $issue_node = node_load($nid->nid);
- // Tags on the issue node should be referenced in field_issue_tags[].
- if (!empty($issue_node->field_issue_tags['und'])) {
- foreach ($issue_node->field_issue_tags['und'] as $tag_info) {
- if (in_array($tag_info['tid'], $status_tids)) {
- // If this was a status tid, look at which group it is in,
- // in which case we found where to categorize the node.
- foreach ($columns as $title => $status_map) {
- if (in_array($tag_info['tid'], $status_map[0])) {
- // Save the whole node object categorized under the $columns
- // $title for this column and move two levels up to the next
- // node.
- $nodes[$title][] = $issue_node;
- break 2;
- }
- }
- }
- }
- }
- }
-
- // Now sort the $nodes array into two tables and fill in the table
- // headers and cells appropriately.
- $header_data = array();
- $table_data = array();
- foreach ($columns as $header => $columns_data) {
- // Sort nodes by "priority" defined by current focus as most important and
- // then major and critical being slightly less important and then the rest.
- $cell = array(-10 => '', -7 => '', -5 => '', 0 => '');
- if (!empty($nodes[$header])) {
- foreach ($nodes[$header] as $issue_node) {
- $classes = 'rocketship-node';
- $cell_terms = '';
- $priority = 0;
- foreach ($issue_node->field_issue_tags['und'] as $tag_info) {
- $term = taxonomy_term_load($tag_info['tid']);
- $term_class = 'rocketship-term';
- switch($term->name) {
- // Style the needs tests tag specially.
- // @todo: this has some more potential, think about it.
- case 'Needs tests':
- $term_class .= ' rocketship-needs-tests';
- break;
- // Mark issues with major and critical status.
- case 'major':
- $classes .= ' rocketship-major';
- $priority = min(-5, $priority);
- break;
- case 'critical':
- $classes .= ' rocketship-critical';
- $priority = min(-7, $priority);
- break;
- // Use the "sprint" tag to mark issues for current focus.
- case 'sprint':
- $classes .= ' rocketship-focus';
- $priority = min(-10, $priority);
- break;
- }
- // Remove tags that are "trivial" in this listing. The master tag
- // is always trivial, the tool focuses on 8.x issues, so those
- // not having that tag should be assumed to be 8.x, RTBC and
- // needs review issues are in their respective column, so that is
- // already represented, and the current page tag is presented on
- // the page, so again no need to repeat it on all nodes (it is not
- // a distinguishing factor).
- if (!in_array($term->name, array(variable_get('rocketship_master_tag', 'D8MI'), 'reviewed & tested by the community', 'needs review', '8.x-dev', $term_for_list->name))) {
- // Add up all terms with their respective tags.
- $cell_terms .= ''. check_plain($term->name) .'';
- }
- }
-
- // Add rocketship-normal class for easier styling.
- if ($priority == 0) {
- $classes .= ' rocketship-normal';
- }
-
- // Link to drupal.org node for this issue.
- $drupalorg_nid = preg_replace('!#(\d+):(.*)$!', '\1', $issue_node->title);
- $cell[$priority] .= ''. l($issue_node->title, 'http://drupal.org/node/'. $drupalorg_nid) .'
';
- $cell[$priority] .= $cell_terms;
- if (!empty($issue_node->field_assigned['und'][0]['value']) && $issue_node->field_assigned['und'][0]['value'] != 'Unassigned') {
- $cell[$priority] .= '
' . t('Assigned to @assigned', array('@assigned' => $issue_node->field_assigned['und'][0]['value'])) . "
";
- }
- $cell[$priority] .= '
';
- }
- }
-
- // Store this cell and header in the right table. Each table will only
- // have one cell per column (which might not look nice), but it gets us
- // to the best display results. For purity's sake we might want to
- // rebuild this based on a three column layout, that is what we
- // accomplish with the tables basically.
- $table_data[$columns_data[1]][] = join('', $cell);
- $header_data[$columns_data[1]][] = $header;
- }
+ return $path;
+}
- // Styling and some usabiltiy improvement JS included inline. This is indeed
- // very ugly but is quickly accessible for development and can be refactored
- // later.
- $output = <<
-STYLE;
-
- // Produce a short legend to be included with each table.
- $legend = 'Legend:
Critical issue
Major issue
Current top priority
';
- foreach($tables as $num => $title) {
- // Lead the table with a legend.
- $output .= $legend . ''. $title .'
';
- // Link to drupal.org issue list for the same issue overview. Reassures
- // users that there is no special data showcased here, they can still
- // access all this on drupal.org.
- $issue_list_url = 'http://drupal.org/project/issues/search/drupal?issue_tags=' . urlencode($term_for_list->name) . ($term_for_list->name == variable_get('rocketship_master_tag', 'D8MI') ? '' : '%2C+' . variable_get('rocketship_master_tag', 'D8MI') . '&issue_tags_op=and');
- $output .= 'See all these issues also at ' .check_plain($issue_list_url) . '. This view of issues is automatically cached and might be out of date up to two hours at times.
';
-
- // Theme the respective "table" with responsive markup.
- $output .= '';
- foreach ($header_data[$num] as $i => $cell) {
- $output .= '
' . $cell . '
';
- if (empty($table_data[$num][$i])) {
- $output .= t('(None)');
- }
- else {
- $output .= $table_data[$num][$i];
- }
- $output .= '';
- }
- $output .= '
';
- // $output .= theme('table', array('header' => $header_data[$num], 'rows' => array($table_data[$num])));
- }
- $output .= '';
-
- // Include username cloud if the sprint tag is displayed.
- $participant_stats = variable_get('rocketship_participant_stats', array());
- if ($term_for_list->name == 'sprint' && !empty($participant_stats)) {
- $lead_user = variable_get('rocketship_lead_user', 'Gábor Hojtsy');
- $participant_uids = variable_get('rocketship_participant_uids', array());
- $participant_recent = variable_get('rocketship_participant_recent', array());
-
- // Remove lead user and some users to avoid skewing the stats.
- if ($lead_user) {
- unset($participant_stats[$lead_user]);
- }
- unset($participant_stats['System Message']);
- unset($participant_stats['Abandoned Projects']);
-
- $usercloud = array();
- $max = max(array_values($participant_stats));
- $steps = array(1, 2, 3, 5, 7, 10);
- foreach ($steps as $i => $step) {
- foreach ($participant_stats as $name => $count) {
- if (($step == 10) || ($count <= $step * ($max/10))) {
- $class = 'rocketship-cloud-' . ($i+1);
- if (in_array($participant_uids[$name], $participant_recent)) {
- $class .= ' rocketship-cloud-recent';
- }
- $usercloud[$name] = '' . check_plain($name) . '';
- unset($participant_stats[$name]);
- }
+/**
+ * Implements hook_preprocess_node().
+ */
+function rocketship_preprocess_node(&$variables) {
+ if ($variables['type'] == 'rocketship_issue' && !$variables['page']) {
+ $drupalorg_nid = preg_replace('!#(\d+):(.*)$!', '\1', $variables['title']);
+ $variables['node_url'] = 'http://drupal.org/node/' . $drupalorg_nid;
+
+ $rocketship_tags = field_get_items('node', $variables['node'], 'rocketship_tags');
+ if ($rocketship_tags) {
+ foreach ($rocketship_tags as $tag_info) {
+ $term = taxonomy_term_load($tag_info['tid']);
+ switch ($term->name) {
+ // Style the needs tests tag specially.
+ // @todo: this has some more potential, think about it.
+ case 'Needs tests':
+ $variables['classes_array'][] = 'node-rocketship-issue-needs-tests';
+ break;
+ // Mark issues with major and critical status.
+ case 'major':
+ $variables['classes_array'][] = 'node-rocketship-issue-major';
+ break;
+ case 'critical':
+ $variables['classes_array'][] = 'node-rocketship-issue-critical';
+ break;
+ // Use the "sprint" tag to mark issues for current focus.
+ case 'sprint':
+ $variables['classes_array'][] = 'node-rocketship-issue-focus';
+ break;
}
}
- // Sort by username in a case insensitive fashion.
- uksort($usercloud, "strnatcasecmp");
- $note = '' . t('Based on participation in all @tag issues (font size) using a quasi-logarithmic scale and on recent activity (boldness).', array('@tag' => variable_get('rocketship_master_tag', 'D8MI')));
- if ($lead_user) {
- $note .= ' ' . t('Excludes the initiative lead, @lead.', array('@lead' => $lead_user));
- }
- $note .= '
';
- $output .= '' . t('Thanks to') . ' #
' . '
' . join(' ', $usercloud) . '
' . $note . '
';
}
-
- // Save the generated page section in the rocketship cache.
- cache_set('rocketship:' . $tid_for_list, $output);
- watchdog('rocketship', 'Rocketship cache miss.');
-
- $node->content['rocketship_issues'] = array(
- '#markup' => $output,
- // Place at the end of the page before book navigation (if present).
- '#weight' => 80,
- );
}
}
diff --git a/rocketship.pages_default.inc b/rocketship.pages_default.inc
new file mode 100644
index 0000000..449c582
--- /dev/null
+++ b/rocketship.pages_default.inc
@@ -0,0 +1,388 @@
+disabled = FALSE; /* Edit this to true to make a default handler disabled initially */
+ $handler->api_version = 1;
+ $handler->name = 'node_view_panel_context';
+ $handler->task = 'node_view';
+ $handler->subtask = '';
+ $handler->handler = 'panel_context';
+ $handler->weight = 0;
+ $handler->conf = array(
+ 'title' => 'Issues',
+ 'no_blocks' => 0,
+ 'pipeline' => 'standard',
+ 'body_classes_to_remove' => '',
+ 'body_classes_to_add' => '',
+ 'css_id' => '',
+ 'css' => '',
+ 'contexts' => array(),
+ 'relationships' => array(
+ 0 => array(
+ 'identifier' => 'Display Tags',
+ 'keyword' => 'tags',
+ 'name' => 'terms_from_node',
+ 'vocabulary' => array(
+ 'rocketship_tags' => 'rocketship_tags',
+ ),
+ 'concatenator' => ',',
+ 'context' => 'argument_entity_id:node_1',
+ 'id' => 1,
+ ),
+ ),
+ 'access' => array(
+ 'plugins' => array(
+ 0 => array(
+ 'name' => 'entity_bundle:node',
+ 'settings' => array(
+ 'type' => array(
+ 'rocketship_focus' => 'rocketship_focus',
+ ),
+ ),
+ 'context' => 'argument_entity_id:node_1',
+ 'not' => FALSE,
+ ),
+ ),
+ 'logic' => 'and',
+ ),
+ );
+ $display = new panels_display();
+ $display->layout = 'rocketship';
+ $display->layout_settings = array();
+ $display->panel_settings = array(
+ 'style_settings' => array(
+ 'default' => NULL,
+ 'middle' => NULL,
+ 'left' => NULL,
+ 'right' => NULL,
+ 'top' => NULL,
+ 'bottom' => NULL,
+ 'upper_first' => NULL,
+ 'upper_middle' => NULL,
+ 'upper_last' => NULL,
+ 'lower_first' => NULL,
+ 'lower_middle' => NULL,
+ 'lower_last' => NULL,
+ ),
+ );
+ $display->cache = array();
+ $display->title = '%node:title';
+ $display->content = array();
+ $display->panels = array();
+ $pane = new stdClass();
+ $pane->pid = 'new-1';
+ $pane->panel = 'bottom';
+ $pane->type = 'rocketship_thanks';
+ $pane->subtype = 'rocketship_thanks';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-1'] = $pane;
+ $display->panels['bottom'][0] = 'new-1';
+ $pane = new stdClass();
+ $pane->pid = 'new-2';
+ $pane->panel = 'lower_first';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'patch (to be ported), 6.x-dev, 7.x-dev',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'Backport',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-2'] = $pane;
+ $display->panels['lower_first'][0] = 'new-2';
+ $pane = new stdClass();
+ $pane->pid = 'new-3';
+ $pane->panel = 'lower_last';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'fixed, closed (duplicate), closed (won\'t fix), closed (works as designed), closed (cannot reproduce), closed (fixed)',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'Closed',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-3'] = $pane;
+ $display->panels['lower_last'][0] = 'new-3';
+ $pane = new stdClass();
+ $pane->pid = 'new-4';
+ $pane->panel = 'lower_middle';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'postponed, postponed (maintainer needs more info)',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'Postponed',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-4'] = $pane;
+ $display->panels['lower_middle'][0] = 'new-4';
+ $pane = new stdClass();
+ $pane->pid = 'new-5';
+ $pane->panel = 'middle';
+ $pane->type = 'rocketship_legend';
+ $pane->subtype = 'rocketship_legend';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-5'] = $pane;
+ $display->panels['middle'][0] = 'new-5';
+ $pane = new stdClass();
+ $pane->pid = 'new-6';
+ $pane->panel = 'middle';
+ $pane->type = 'rocketship_link';
+ $pane->subtype = 'rocketship_link';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'override_title' => 1,
+ 'override_title_text' => 'Other related issues',
+ 'context' => 'empty',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 1;
+ $pane->locks = array();
+ $display->content['new-6'] = $pane;
+ $display->panels['middle'][1] = 'new-6';
+ $pane = new stdClass();
+ $pane->pid = 'new-7';
+ $pane->panel = 'top';
+ $pane->type = 'node_content';
+ $pane->subtype = 'node_content';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'links' => 1,
+ 'no_extras' => 1,
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ 'identifier' => '',
+ 'link' => 0,
+ 'leave_node_title' => 0,
+ 'build_mode' => 'full',
+ 'context' => 'argument_entity_id:node_1',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-7'] = $pane;
+ $display->panels['top'][0] = 'new-7';
+ $pane = new stdClass();
+ $pane->pid = 'new-8';
+ $pane->panel = 'top';
+ $pane->type = 'rocketship_legend';
+ $pane->subtype = 'rocketship_legend';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 1;
+ $pane->locks = array();
+ $display->content['new-8'] = $pane;
+ $display->panels['top'][1] = 'new-8';
+ $pane = new stdClass();
+ $pane->pid = 'new-9';
+ $pane->panel = 'top';
+ $pane->type = 'rocketship_link';
+ $pane->subtype = 'rocketship_link';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'override_title' => 1,
+ 'override_title_text' => 'Currently in the works for Drupal 8',
+ 'context' => 'relationship_terms_from_node_1',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 2;
+ $pane->locks = array();
+ $display->content['new-9'] = $pane;
+ $display->panels['top'][2] = 'new-9';
+ $pane = new stdClass();
+ $pane->pid = 'new-10';
+ $pane->panel = 'upper_first';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'active, needs work',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'To do',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-10'] = $pane;
+ $display->panels['upper_first'][0] = 'new-10';
+ $pane = new stdClass();
+ $pane->pid = 'new-11';
+ $pane->panel = 'upper_last';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'reviewed & tested by the community',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'To be committed',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-11'] = $pane;
+ $display->panels['upper_last'][0] = 'new-11';
+ $pane = new stdClass();
+ $pane->pid = 'new-12';
+ $pane->panel = 'upper_middle';
+ $pane->type = 'views_panes';
+ $pane->subtype = 'issues-focus';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'exposed' => array(
+ 'rocketship_tags_tid' => 'needs review',
+ ),
+ 'context' => array(
+ 0 => 'relationship_terms_from_node_1',
+ ),
+ 'override_title' => 1,
+ 'override_title_text' => 'To review',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ 'style' => 'default',
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $pane->locks = array();
+ $display->content['new-12'] = $pane;
+ $display->panels['upper_middle'][0] = 'new-12';
+ $display->hide_title = PANELS_TITLE_NONE;
+ $display->title_pane = '0';
+ $handler->conf['display'] = $display;
+
+ $export['node_view_panel_context'] = $handler;
+
+ return $export;
+}
diff --git a/rocketship.views_default.inc b/rocketship.views_default.inc
new file mode 100644
index 0000000..4ca82e0
--- /dev/null
+++ b/rocketship.views_default.inc
@@ -0,0 +1,130 @@
+name = 'issues';
+ $view->description = '';
+ $view->tag = 'Rocketship';
+ $view->base_table = 'node';
+ $view->human_name = 'Issues';
+ $view->core = 7;
+ $view->api_version = '3.0';
+ $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+
+ /* Display: Master */
+ $handler = $view->new_display('default', 'Master', 'default');
+ $handler->display->display_options['use_more_always'] = FALSE;
+ $handler->display->display_options['access']['type'] = 'perm';
+ $handler->display->display_options['cache']['type'] = 'none';
+ $handler->display->display_options['query']['type'] = 'views_query';
+ $handler->display->display_options['exposed_form']['type'] = 'basic';
+ $handler->display->display_options['pager']['type'] = 'none';
+ $handler->display->display_options['pager']['options']['offset'] = '0';
+ $handler->display->display_options['style_plugin'] = 'default';
+ $handler->display->display_options['row_plugin'] = 'node';
+ $handler->display->display_options['row_options']['links'] = FALSE;
+ /* No results behavior: Global: Text area */
+ $handler->display->display_options['empty']['area']['id'] = 'area';
+ $handler->display->display_options['empty']['area']['table'] = 'views';
+ $handler->display->display_options['empty']['area']['field'] = 'area';
+ $handler->display->display_options['empty']['area']['empty'] = TRUE;
+ $handler->display->display_options['empty']['area']['content'] = '(None)';
+ $handler->display->display_options['empty']['area']['format'] = 'plain_text';
+ /* Field: Content: Title */
+ $handler->display->display_options['fields']['title']['id'] = 'title';
+ $handler->display->display_options['fields']['title']['table'] = 'node';
+ $handler->display->display_options['fields']['title']['field'] = 'title';
+ $handler->display->display_options['fields']['title']['label'] = '';
+ $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
+ $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
+ /* Sort criterion: Content: Priority (rocketship_priority) */
+ $handler->display->display_options['sorts']['rocketship_priority_value']['id'] = 'rocketship_priority_value';
+ $handler->display->display_options['sorts']['rocketship_priority_value']['table'] = 'field_data_rocketship_priority';
+ $handler->display->display_options['sorts']['rocketship_priority_value']['field'] = 'rocketship_priority_value';
+ /* Sort criterion: Content: Updated date */
+ $handler->display->display_options['sorts']['changed']['id'] = 'changed';
+ $handler->display->display_options['sorts']['changed']['table'] = 'node';
+ $handler->display->display_options['sorts']['changed']['field'] = 'changed';
+ $handler->display->display_options['sorts']['changed']['order'] = 'DESC';
+ /* Contextual filter: Content: Has taxonomy term ID */
+ $handler->display->display_options['arguments']['tid']['id'] = 'tid';
+ $handler->display->display_options['arguments']['tid']['table'] = 'taxonomy_index';
+ $handler->display->display_options['arguments']['tid']['field'] = 'tid';
+ $handler->display->display_options['arguments']['tid']['default_action'] = 'empty';
+ $handler->display->display_options['arguments']['tid']['default_argument_type'] = 'fixed';
+ $handler->display->display_options['arguments']['tid']['summary']['number_of_records'] = '0';
+ $handler->display->display_options['arguments']['tid']['summary']['format'] = 'default_summary';
+ $handler->display->display_options['arguments']['tid']['summary_options']['items_per_page'] = '25';
+ $handler->display->display_options['arguments']['tid']['specify_validation'] = TRUE;
+ $handler->display->display_options['arguments']['tid']['validate']['type'] = 'taxonomy_term';
+ $handler->display->display_options['arguments']['tid']['validate_options']['vocabularies'] = array(
+ 'rocketship_tags' => 'rocketship_tags',
+ );
+ $handler->display->display_options['arguments']['tid']['validate']['fail'] = 'empty';
+ $handler->display->display_options['filter_groups']['operator'] = 'OR';
+ /* Filter criterion: Content: Priority (rocketship_priority) */
+ $handler->display->display_options['filters']['rocketship_priority_value']['id'] = 'rocketship_priority_value';
+ $handler->display->display_options['filters']['rocketship_priority_value']['table'] = 'field_data_rocketship_priority';
+ $handler->display->display_options['filters']['rocketship_priority_value']['field'] = 'rocketship_priority_value';
+ $handler->display->display_options['filters']['rocketship_priority_value']['operator'] = '<';
+ $handler->display->display_options['filters']['rocketship_priority_value']['value']['value'] = '0';
+ $handler->display->display_options['filters']['rocketship_priority_value']['group'] = 1;
+ /* Filter criterion: Content: Type */
+ $handler->display->display_options['filters']['type']['id'] = 'type';
+ $handler->display->display_options['filters']['type']['table'] = 'node';
+ $handler->display->display_options['filters']['type']['field'] = 'type';
+ $handler->display->display_options['filters']['type']['value'] = array(
+ 'rocketship_issue' => 'rocketship_issue',
+ );
+ $handler->display->display_options['filters']['type']['group'] = 1;
+ /* Filter criterion: Content: Published */
+ $handler->display->display_options['filters']['status']['id'] = 'status';
+ $handler->display->display_options['filters']['status']['table'] = 'node';
+ $handler->display->display_options['filters']['status']['field'] = 'status';
+ $handler->display->display_options['filters']['status']['value'] = 1;
+ $handler->display->display_options['filters']['status']['group'] = 1;
+ $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
+ /* Filter criterion: Content: Tags (rocketship_tags) */
+ $handler->display->display_options['filters']['rocketship_tags_tid']['id'] = 'rocketship_tags_tid';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['table'] = 'field_data_rocketship_tags';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['field'] = 'rocketship_tags_tid';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['value'] = '';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['group'] = 1;
+ $handler->display->display_options['filters']['rocketship_tags_tid']['exposed'] = TRUE;
+ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['operator_id'] = 'rocketship_tags_tid_op';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['label'] = 'Tags';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['operator'] = 'rocketship_tags_tid_op';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['expose']['identifier'] = 'rocketship_tags_tid';
+ $handler->display->display_options['filters']['rocketship_tags_tid']['vocabulary'] = 'rocketship_tags';
+
+ /* Display: Focus */
+ $handler = $view->new_display('panel_pane', 'Focus', 'focus');
+ $handler->display->display_options['pane_title'] = 'Taged Issues';
+ $handler->display->display_options['allow']['use_pager'] = 0;
+ $handler->display->display_options['allow']['items_per_page'] = 0;
+ $handler->display->display_options['allow']['offset'] = 0;
+ $handler->display->display_options['allow']['link_to_view'] = 0;
+ $handler->display->display_options['allow']['more_link'] = 0;
+ $handler->display->display_options['allow']['path_override'] = 0;
+ $handler->display->display_options['allow']['title_override'] = 'title_override';
+ $handler->display->display_options['allow']['exposed_form'] = 'exposed_form';
+ $handler->display->display_options['allow']['fields_override'] = 0;
+ $handler->display->display_options['argument_input'] = array(
+ 'tid' => array(
+ 'type' => 'context',
+ 'context' => 'entity:taxonomy_term.tid',
+ 'context_optional' => 0,
+ 'panel' => '0',
+ 'fixed' => '',
+ 'label' => 'Taxonomy term: Term ID',
+ ),
+ );
+ $handler->display->display_options['inherit_panels_path'] = '1';
+
+ $views[$view->name] = $view;
+ return $views;
+}