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; +}