diff --git a/core/modules/node/config/views.view.content_revisions.yml b/core/modules/node/config/views.view.content_revisions.yml new file mode 100644 index 0000000..ab84e6f --- /dev/null +++ b/core/modules/node/config/views.view.content_revisions.yml @@ -0,0 +1,198 @@ +api_version: '3.0' +base_field: vid +base_table: node_revision +core: 8.x +description: '' +disabled: '0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: perm + perm: 'view revisions' + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: none + options: + offset: '0' + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: '1' + row_class_special: '1' + override: '1' + sticky: '0' + summary: '' + columns: + timestamp: timestamp + title: title + name: name + log: title + revert_revision: revert_revision + delete_revision: delete_revision + dropbutton: dropbutton + row: + type: fields + fields: + timestamp: + id: timestamp + table: node_revision + field: timestamp + label: '' + exclude: '1' + date_format: short + name: + id: name + table: users + field: name + relationship: uid + label: '' + exclude: '1' + format_username: '1' + title: + id: title + table: node_revision + field: title + label: Revision + exclude: '0' + alter: + alter_text: '1' + text: '[timestamp] by [name]' + link_to_node_revision: '1' + log: + id: log + table: node_revision + field: log + label: '' + exclude: '0' + element_type: p + element_class: revision-log + revert_revision: + id: revert_revision + table: node_revision + field: revert_revision + label: '' + exclude: '1' + text: revert + delete_revision: + id: delete_revision + table: node_revision + field: delete_revision + exclude: '1' + text: delete + dropbutton: + id: dropbutton + table: views + field: dropbutton + label: Operations + empty: 'current revision' + fields: + revert_revision: revert_revision + delete_revision: delete_revision + timestamp: '0' + title: '0' + name: '0' + destination: '0' + filters: + status: + value: '1' + table: node_revision + field: status + id: status + expose: + operator: '0' + group: '1' + sorts: + timestamp: + id: timestamp + table: node_revision + field: timestamp + relationship: none + group_type: group + admin_label: '' + order: DESC + exposed: '0' + expose: + label: '' + granularity: second + title: 'Content revisions' + arguments: + nid: + id: nid + table: node_revision + field: nid + relationship: none + group_type: group + admin_label: '' + default_action: 'not found' + exception: + value: all + title_enable: '0' + title: All + title_enable: '1' + title: 'Revisions for %1' + breadcrumb_enable: '0' + breadcrumb: '' + default_argument_type: node + default_argument_options: '' + default_argument_skip_url: '0' + summary_options: + base_path: '' + count: '1' + items_per_page: '25' + override: '0' + summary: + sort_order: asc + number_of_records: '0' + format: default_summary + specify_validation: '1' + validate: + type: node + fail: 'not found' + validate_options: + access: '1' + access_op: update + nid_type: nid + types: { } + break_phrase: '0' + not: '0' + relationships: + uid: + id: uid + table: node_revision + field: uid + relationship: none + group_type: group + admin_label: '' + label: 'revision user' + required: '0' + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: '' + display_options: + path: node/%/revisions + menu: + type: tab + title: Revisions + description: '' + name: admin + weight: '0' + context: '0' + enabled: '1' +human_name: 'Content revisions' +module: node +name: content_revisions +tag: '' diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php index 2bb53f1..50f37d6 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkDelete.php @@ -27,7 +27,9 @@ public function access() { } function render_link($data, $values) { - list($node, $vid) = $this->get_revision_entity($values, 'delete'); + $node = node_load($values->node_revision_nid); + $vid = $values->vid; + //list($node, $vid) = $this->get_revision_entity($values, 'delete'); if (!isset($vid)) { return; } diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php index 11e66ff..8ea8eef 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/RevisionLinkRevert.php @@ -27,7 +27,9 @@ public function access() { } function render_link($data, $values) { - list($node, $vid) = $this->get_revision_entity($values, 'update'); + $node = node_load($values->node_revision_nid); + $vid = $values->vid; + //list($node, $vid) = $this->get_revision_entity($values, 'update'); if (!isset($vid)) { return; } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php index 2ce2abb..548bcdc 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php @@ -72,6 +72,8 @@ function setUp() { * Checks node revision related operations. */ function testRevisions() { + module_enable(array('views')); + $nodes = $this->nodes; $logs = $this->logs; diff --git a/core/modules/node/node.module b/core/modules/node/node.module index a37679c..aa60fc1 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1752,16 +1752,6 @@ function node_menu() { 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', ); - $items['node/%node/revisions'] = array( - 'title' => 'Revisions', - 'page callback' => 'node_revision_overview', - 'page arguments' => array(1), - 'access callback' => '_node_revision_access', - 'access arguments' => array(1), - 'weight' => 2, - 'type' => MENU_LOCAL_TASK, - 'file' => 'node.pages.inc', - ); $items['node/%node/revisions/%node_revision/view'] = array( 'title' => 'Revisions', 'page callback' => 'node_show', @@ -1848,25 +1838,6 @@ function node_last_changed($nid) { } /** - * Returns a list of all the existing revision numbers for the node passed in. - * - * @param Drupal\node\Node $node - * The node entity. - * - * @return - * An associative array keyed by node revision number. - */ -function node_revision_list(Node $node) { - $revisions = array(); - $result = db_query('SELECT r.vid, r.title, r.log, r.uid, n.vid AS current_vid, r.timestamp, u.name FROM {node_revision} r LEFT JOIN {node} n ON n.vid = r.vid INNER JOIN {users} u ON u.uid = r.uid WHERE r.nid = :nid ORDER BY r.vid DESC', array(':nid' => $node->nid)); - foreach ($result as $revision) { - $revisions[$revision->vid] = $revision; - } - - return $revisions; -} - -/** * Implements hook_block_info(). */ function node_block_info() { diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc index 0a18ec1..ddf397f 100644 --- a/core/modules/node/node.pages.inc +++ b/core/modules/node/node.pages.inc @@ -237,81 +237,6 @@ function node_delete_confirm_submit($form, &$form_state) { } /** - * Page callback: Generates an overview table of older revisions of a node. - * - * @param object $node - * A node object. - * - * @return array - * An array as expected by drupal_render(). - * - * @see node_menu() - */ -function node_revision_overview($node) { - drupal_set_title(t('Revisions for %title', array('%title' => $node->label())), PASS_THROUGH); - - $header = array(t('Revision'), t('Operations')); - - $revisions = node_revision_list($node); - - $rows = array(); - $type = $node->type; - - $revert_permission = FALSE; - if ((user_access("revert $type revisions") || user_access('revert all revisions') || user_access('administer nodes')) && node_access('update', $node)) { - $revert_permission = TRUE; - } - $delete_permission = FALSE; - if ((user_access("delete $type revisions") || user_access('delete all revisions') || user_access('administer nodes')) && node_access('delete', $node)) { - $delete_permission = TRUE; - } - foreach ($revisions as $revision) { - $row = array(); - $type = $node->type; - if ($revision->current_vid > 0) { - $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision)))) - . (($revision->log != '') ? '

' . filter_xss($revision->log) . '

' : ''), - 'class' => array('revision-current')); - $row[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current')); - } - else { - $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid/revisions/$revision->vid/view"), '!username' => theme('username', array('account' => $revision)))) - . (($revision->log != '') ? '

' . filter_xss($revision->log) . '

' : ''); - if ($revert_permission) { - $links['revert'] = array( - 'title' => t('revert'), - 'href' => "node/$node->nid/revisions/$revision->vid/revert", - ); - } - if ($delete_permission) { - $links['delete'] = array( - 'title' => t('delete'), - 'href' => "node/$node->nid/revisions/$revision->vid/delete", - ); - } - $row[] = array( - 'data' => array( - '#type' => 'operations', - '#links' => $links, - ), - ); - } - $rows[] = $row; - } - - $build['node_revisions_table'] = array( - '#theme' => 'table', - '#rows' => $rows, - '#header' => $header, - '#attached' => array ( - 'css' => array(drupal_get_path('module', 'node') . '/node.admin.css'), - ), - ); - - return $build; -} - -/** * Page callback: Form constructor for the reversion confirmation form. * * This form prevents against CSRF attacks. diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/field/Dropbutton.php b/core/modules/system/lib/Drupal/system/Plugin/views/field/Dropbutton.php new file mode 100644 index 0000000..09771e4 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/views/field/Dropbutton.php @@ -0,0 +1,42 @@ +getLinks(); + + if (!empty($links)) { + return array( + '#type' => 'dropbutton', + '#links' => $links, + ); + } + else { + return ''; + } + } + +} diff --git a/core/modules/system/system.views.inc b/core/modules/system/system.views.inc new file mode 100644 index 0000000..38ba502 --- /dev/null +++ b/core/modules/system/system.views.inc @@ -0,0 +1,21 @@ + t('Dropbutton'), + 'help' => t('Display fields in a dropbutton.'), + 'field' => array( + 'id' => 'dropbutton', + ), + ); +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php index 64f83b2..1073f04 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php @@ -844,14 +844,13 @@ public function buildOptionsForm(&$form, &$form_state) { // Get a list of the available fields and arguments for token replacement. - $options = array(); - foreach ($this->view->display_handler->getHandlers('field') as $field => $handler) { - $options[t('Fields')]["[$field]"] = $handler->adminLabel(); - // We only use fields up to (and including) this one. - if ($field == $this->options['id']) { - break; - } + + // Setup the tokens for fields. + $previous = $this->getPreviousFieldLabels(); + foreach ($previous as $id => $label) { + $options[t('Fields')]["[$id]"] = $label; } + $count = 0; // This lets us prepare the key as we want it printed. foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) { $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel())); @@ -1073,6 +1072,18 @@ public function buildOptionsForm(&$form, &$form_state) { } /** + * Returns all field lables of fields before this field. + * + * @return array + * An array of field labels keyed by their field IDs. + */ + protected function getPreviousFieldLabels() { + $all_fields = $this->view->display_handler->getFieldLabels(); + $field_options = array_slice($all_fields, 0, array_search($this->options['id'], array_keys($all_fields))); + return $field_options; + } + + /** * Provide extra data to the administration form */ public function adminSummary() { diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php new file mode 100644 index 0000000..a33ac65 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php @@ -0,0 +1,90 @@ + array()); + $options['destination'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + /** + * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::defineOptions(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + // Only show fields that precede this one. + $field_options = $this->getPreviousFieldLabels(); + $form['fields'] = array( + '#type' => 'checkboxes', + '#title' => t('Fields'), + '#description' => t('Fields to be included as links.'), + '#options' => $field_options, + '#default_value' => $this->options['fields'], + ); + $form['destination'] = array( + '#type' => 'checkbox', + '#title' => t('Include destination'), + '#description' => t('Include a "destination" parameter in the link to return the user to the original view upon completing the link action.'), + '#default_value' => $this->options['destination'], + ); + } + + /** + * Gets the list of links used by this field. + * + * @return array + * The links which are used by the render function. + */ + protected function getLinks() { + $links = array(); + foreach ($this->options['fields'] as $field) { + if (empty($this->view->field[$field]->last_render_text)) { + continue; + } + $title = $this->view->field[$field]->last_render_text; + $path = ''; + if (!empty($this->view->field[$field]->options['alter']['path'])) { + $path = $this->view->field[$field]->options['alter']['path']; + } + // Make sure that tokens are replaced for this paths as well. + $tokens = $this->get_render_tokens(array()); + $path = strip_tags(decode_entities(strtr($path, $tokens))); + + $links[$field] = array( + 'href' => $path, + 'title' => $title, + ); + if (!empty($this->options['destination'])) { + $links[$field]['query'] = drupal_get_destination(); + } + } + + return $links; + } + + /** + * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::query(). + */ + public function query() { + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/System/FieldDropButtonTest.php b/core/modules/views/lib/Drupal/views/Tests/System/FieldDropButtonTest.php new file mode 100644 index 0000000..6ff01f1 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/System/FieldDropButtonTest.php @@ -0,0 +1,53 @@ + 'System: Dropbutton', + 'description' => 'Tests the dropbutton field handler.', + 'group' => 'Views Modules', + ); + } + + /** + * Tests dropbutton field. + */ + public function testDropbutton() { + // Create some test nodes. + $nodes = array(); + for ($i = 0; $i < 5; $i++) { + $nodes[] = $this->drupalCreateNode(); + } + + $this->drupalGet('test-dropbutton'); + foreach ($nodes as $node) { + $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path) and text()=:title]', array(':path' => '/node/' . $node->id(), ':title' => $node->label())); + $this->assertEqual(count($result), 1, 'Just one node title link was found.'); + $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path) and text()=:title]', array(':path' => '/node/' . $node->id(), ':title' => t('Custom Text'))); + $this->assertEqual(count($result), 1, 'Just one custom link was found.'); + } + } + +} diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_dropbutton.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_dropbutton.yml new file mode 100755 index 0000000..2d4fe32 --- /dev/null +++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_dropbutton.yml @@ -0,0 +1,227 @@ +api_version: '3.0' +base_field: nid +base_table: node +core: 8.x +description: '' +disabled: '0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: perm + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + options: + items_per_page: '10' + style: + type: default + row: + type: fields + fields: + nid: + id: nid + table: node + field: nid + relationship: none + group_type: group + admin_label: '' + label: Nid + exclude: '1' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + link_to_node: '0' + title: + id: title + table: node + field: title + label: '' + alter: + alter_text: '0' + make_link: '0' + absolute: '0' + trim: '0' + word_boundary: '0' + ellipsis: '0' + strip_tags: '0' + html: '0' + hide_empty: '0' + empty_zero: '0' + link_to_node: '1' + nothing: + id: nothing + table: views + field: nothing + relationship: none + group_type: group + admin_label: '' + label: 'Custom text' + exclude: '0' + alter: + alter_text: '1' + text: 'Custom Text' + make_link: '1' + path: 'node/[nid]' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '0' + dropbutton: + id: dropbutton + table: views + field: dropbutton + relationship: none + group_type: group + admin_label: '' + label: Dropbutton + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + fields: + title: title + nothing: nothing + nid: '0' + destination: '1' + filters: + status: + value: '1' + table: node + field: status + id: status + expose: + operator: '0' + group: '1' + sorts: + created: + id: created + table: node + field: created + order: DESC + title: test_dropbutton + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: '' + display_options: + path: test-dropbutton + field: + title: + link_to_node: '0' +human_name: test_dropbutton +module: views +name: test_dropbutton +tag: ''