diff --git a/core/modules/views/config/views.settings.yml b/core/modules/views/config/views.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b9c81c5d650fd5cf1caba70232bdab1794528de
--- /dev/null
+++ b/core/modules/views/config/views.settings.yml
@@ -0,0 +1,35 @@
+debug:
+ output: '0'
+ region: 'footer'
+display_extenders: { }
+no_javascript: '0'
+skip_cache: '0'
+sql_signature: '0'
+ui:
+ show:
+ additional_queries: '0'
+ advanced_column: '0'
+ listing_filters: '0'
+ master_display: '0'
+ performance_statistics: '0'
+ preview_information: '1'
+ sql_query:
+ enabled: '0'
+ where: 'above'
+ display_embed: '0'
+ custom_theme: '_default'
+ always_live_preview: '1'
+ always_live_preview_button: '1'
+ exposed_filter_any_label: 'new_any'
+field_rewrite_elements:
+ div: DIV
+ span: SPAN
+ h1: H1
+ h2: H2
+ h3: H3
+ h4: H4
+ h5: H5
+ h6: H6
+ p: P
+ strong: STRONG
+ em: EM
diff --git a/core/modules/views/config/views.view.archive.yml b/core/modules/views/config/views.view.archive.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f2b3a62ed5bfb91c789579cf11e4f6594a55590b
--- /dev/null
+++ b/core/modules/views/config/views.view.archive.yml
@@ -0,0 +1,103 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: archive
+description: 'Display a list of months that link to content for that month.'
+tag: default
+base_table: node
+human_name: Archive
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Monthly archive'
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ sorts:
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ arguments:
+ created_year_month:
+ id: created_year_month
+ table: node
+ field: created_year_month
+ default_action: summary
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ sort_order: desc
+ format: default_summary
+ summary_options:
+ override: true
+ items_per_page: '30'
+ specify_validation: 1
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: 1
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: default
+ row:
+ type: node
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: archive
+ block_1:
+ id: block_1
+ display_title: Block
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ arguments: false
+ arguments:
+ created_year_month:
+ id: created_year_month
+ table: node
+ field: created_year_month
+ default_action: summary
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ summary_options:
+ items_per_page: '30'
+ specify_validation: 1
diff --git a/core/modules/views/config/views.view.backlinks.yml b/core/modules/views/config/views.view.backlinks.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8c1f2a76c4e3ebc742d7337d93b006aa63a0a5e8
--- /dev/null
+++ b/core/modules/views/config/views.view.backlinks.yml
@@ -0,0 +1,123 @@
+disabled: true
+api_version: '3.0'
+module: search
+name: backlinks
+description: 'Displays a list of nodes that link to the node, using the search backlinks table.'
+tag: default
+base_table: node
+human_name: Backlinks
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: 30
+ empty:
+ text:
+ id: area
+ table: views
+ field: area
+ empty: false
+ content: 'No backlinks found.'
+ format: '1'
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ label: ''
+ link_to_node: 1
+ arguments:
+ nid:
+ id: nid
+ table: search_node_links_to
+ field: nid
+ default_action: 'not found'
+ title_enable: 1
+ title: 'Pages that link to %1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: node
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: 1
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: html_list
+ options:
+ type: ol
+ row:
+ type: fields
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: node/%/backlinks
+ menu:
+ type: tab
+ title: 'What links here'
+ weight: '0'
+ block_1:
+ id: block
+ display_title: 'What links here'
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ use_more: false
+ style_plugin: false
+ style_options: false
+ row_plugin: false
+ row_options: false
+ arguments: false
+ use_more: true
+ arguments:
+ nid:
+ id: nid
+ table: search_node_links_to
+ field: nid
+ default_action: default
+ title_enable: 1
+ title: 'What links here'
+ default_argument_type: node
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: node
+ style:
+ type: html_list
+ row:
+ type: fields
diff --git a/core/modules/views/config/views.view.comments_recent.yml b/core/modules/views/config/views.view.comments_recent.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6e86a0c980896a677eefcbef802e825a988935f1
--- /dev/null
+++ b/core/modules/views/config/views.view.comments_recent.yml
@@ -0,0 +1,126 @@
+disabled: true
+api_version: '3.0'
+module: comment
+name: comments_recent
+description: 'Contains a block and a page to list recent comments; the block will automatically link to the page, which displays the comment body as well as a link to the node.'
+tag: default
+base_table: comment
+human_name: 'Recent comments'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Recent comments'
+ use_more: true
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: some
+ options:
+ items_per_page: 5
+ relationships:
+ nid:
+ id: nid
+ table: comment
+ field: nid
+ fields:
+ subject:
+ id: subject
+ table: comment
+ field: subject
+ label: ''
+ link_to_comment: 1
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ label: ''
+ date_format: 'time ago'
+ sorts:
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ order: DESC
+ filters:
+ status_extra:
+ id: status_extra
+ table: node
+ field: status_extra
+ relationship: nid
+ group: 0
+ style:
+ type: html_list
+ row:
+ type: fields
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ style_plugin: false
+ style_options: false
+ row_plugin: false
+ row_options: false
+ fields: false
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ relationship: nid
+ label: 'Reply to'
+ link_to_node: 1
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ label: ''
+ date_format: 'time ago'
+ subject:
+ id: subject
+ table: comment
+ field: subject
+ label: ''
+ link_to_comment: 1
+ comment:
+ id: comment
+ table: field_data_comment_body
+ field: comment_body
+ label: ''
+ path: comments/recent
+ style:
+ type: html_list
+ row:
+ type: fields
+ options:
+ inline:
+ title: title
+ timestamp: timestamp
+ separator: ' '
+ block_1:
+ id: block_1
+ display_title: Block
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
diff --git a/core/modules/views/config/views.view.frontpage.yml b/core/modules/views/config/views.view.frontpage.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a635a0ec671cd4c26508e8ac7da9946998dd4c4d
--- /dev/null
+++ b/core/modules/views/config/views.view.frontpage.yml
@@ -0,0 +1,95 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: frontpage
+description: 'Emulates the default Drupal front page; you may set the default home page path to this view to make it your front page.'
+tag: default
+base_table: node
+human_name: 'Front page'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ style:
+ type: default
+ row:
+ type: node
+ options:
+ links: 1
+ sorts:
+ sticky:
+ id: sticky
+ table: node
+ field: sticky
+ order: DESC
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ filters:
+ promote:
+ id: promote
+ table: node
+ field: promote
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ status:
+ id: status
+ table: node
+ field: status
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: frontpage
+ feed_1:
+ id: feed_1
+ display_title: Feed
+ display_plugin: feed
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ title: false
+ title: 'Front page feed'
+ pager:
+ type: some
+ style:
+ type: rss
+ row:
+ type: node_rss
+ path: rss.xml
+ displays:
+ default: default
+ page: page
+ sitename_title: '1'
diff --git a/core/modules/views/config/views.view.glossary.yml b/core/modules/views/config/views.view.glossary.yml
new file mode 100644
index 0000000000000000000000000000000000000000..966da7ced61c14a7ddb74fcac06598d2b572b680
--- /dev/null
+++ b/core/modules/views/config/views.view.glossary.yml
@@ -0,0 +1,150 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: glossary
+description: 'A list of all content, by letter.'
+tag: default
+base_table: node
+human_name: Glossary
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ use_ajax: true
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: 36
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ link_to_node: 1
+ name:
+ id: name
+ table: users
+ field: name
+ label: Author
+ link_to_user: 1
+ relationship: uid
+ changed:
+ id: changed
+ table: node
+ field: changed
+ label: 'Last update'
+ date_format: large
+ arguments:
+ title:
+ id: title
+ table: node
+ field: title
+ default_action: default
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: default_summary
+ specify_validation: 1
+ glossary: 1
+ limit: '1'
+ case: upper
+ path_case: lower
+ transform_dash: 0
+ relationships:
+ uid:
+ id: uid
+ table: node
+ field: uid
+ style:
+ type: table
+ options:
+ columns:
+ title: title
+ name: name
+ changed: changed
+ default: title
+ info:
+ title:
+ sortable: 1
+ separator: ''
+ name:
+ sortable: 1
+ separator: ''
+ changed:
+ sortable: 1
+ separator: ''
+ override: 1
+ sticky: 0
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: glossary
+ menu:
+ type: normal
+ title: Glossary
+ weight: '0'
+ attachment_1:
+ id: attachment_1
+ display_title: Attachment
+ display_plugin: attachment
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ pager:
+ type: none
+ options:
+ offset: '0'
+ defaults:
+ arguments: false
+ arguments:
+ title:
+ id: title
+ table: node
+ field: title
+ default_action: summary
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: unformatted_summary
+ summary_options:
+ items_per_page: '25'
+ inline: 1
+ separator: ' | '
+ specify_validation: 1
+ glossary: 1
+ limit: '1'
+ case: upper
+ path_case: lower
+ transform_dash: 0
+ displays:
+ default: default
+ page: page_1
+ inherit_arguments: 0
diff --git a/core/modules/views/config/views.view.taxonomy_term.yml b/core/modules/views/config/views.view.taxonomy_term.yml
new file mode 100644
index 0000000000000000000000000000000000000000..742c5d74af4f68f120284aac50d9c8b4aeed9666
--- /dev/null
+++ b/core/modules/views/config/views.view.taxonomy_term.yml
@@ -0,0 +1,110 @@
+disabled: true
+api_version: '3.0'
+module: taxonomy
+name: taxonomy_term
+description: 'A view to emulate Drupal core''s handling of taxonomy/term.'
+tag: default
+base_table: node
+human_name: 'Taxonomy term'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ sorts:
+ sticky:
+ id: sticky
+ table: node
+ field: sticky
+ order: DESC
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ arguments:
+ term_node_tid_depth:
+ id: term_node_tid_depth
+ table: node
+ field: term_node_tid_depth
+ default_action: 'not found'
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: taxonomy_term
+ depth: '0'
+ break_phrase: 1
+ term_node_tid_depth_modifier:
+ id: term_node_tid_depth_modifier
+ table: node
+ field: term_node_tid_depth_modifier
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ filters:
+ status_extra:
+ id: status_extra
+ table: node
+ field: status_extra
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: default
+ row:
+ type: node
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: taxonomy/term/%
+ feed_1:
+ id: feed_1
+ display_title: Feed
+ display_plugin: feed
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ pager:
+ type: full
+ options:
+ items_per_page: 15
+ path: taxonomy/term/%/%/feed
+ displays:
+ page: page
+ default: 0
+ style:
+ type: rss
+ row:
+ type: node_rss
diff --git a/core/modules/views/config/views.view.tracker.yml b/core/modules/views/config/views.view.tracker.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5d31e2fdf73e91da4a4c5dfbb1622cb6fb1bcf9e
--- /dev/null
+++ b/core/modules/views/config/views.view.tracker.yml
@@ -0,0 +1,150 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: tracker
+description: 'Shows all new activity on system.'
+tag: default
+base_table: node
+human_name: Tracker
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Recent posts'
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: '25'
+ relationships:
+ uid:
+ id: uid
+ table: node
+ field: uid
+ fields:
+ type:
+ id: type
+ table: node
+ field: type
+ title:
+ id: title
+ table: node
+ field: title
+ name:
+ id: name
+ table: users
+ field: name
+ relationship: uid
+ label: Author
+ comment_count:
+ id: comment_count
+ table: node_comment_statistics
+ field: comment_count
+ label: Replies
+ last_comment_timestamp:
+ id: last_comment_timestamp
+ table: node_comment_statistics
+ field: last_comment_timestamp
+ label: 'Last Post'
+ timestamp:
+ id: timestamp
+ table: history
+ field: timestamp
+ label: ''
+ link_to_node: 0
+ comments: 1
+ new_comments:
+ id: new_comments
+ table: node
+ field: new_comments
+ label: ''
+ hide_empty: true
+ suffix: ' new'
+ link_to_comment: 1
+ sorts:
+ last_comment_timestamp:
+ id: last_comment_timestamp
+ table: node_comment_statistics
+ field: last_comment_timestamp
+ arguments:
+ uid_touch:
+ id: uid_touch
+ table: node
+ field: uid_touch
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: 'Recent posts for %1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: table
+ options:
+ columns:
+ type: type
+ title: title
+ name: name
+ comment_count: comment_count
+ last_comment_timestamp: last_comment_timestamp
+ timestamp: title
+ new_comments: comment_count
+ default: last_comment_timestamp
+ info:
+ type:
+ sortable: 1
+ separator: ''
+ title:
+ sortable: 1
+ separator: ' '
+ name:
+ sortable: 1
+ separator: ''
+ comment_count:
+ sortable: 1
+ separator: '
'
+ last_comment_timestamp:
+ sortable: 1
+ separator: ' '
+ timestamp:
+ separator: ''
+ new_comments:
+ separator: ''
+ override: 1
+ order: desc
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: tracker
+ menu:
+ type: normal
+ title: 'Recent posts'
diff --git a/core/modules/views/css/views.base-rtl.css b/core/modules/views/css/views.base-rtl.css
new file mode 100644
index 0000000000000000000000000000000000000000..3755668729d1a69e3955af196523e5d17acdc1f8
--- /dev/null
+++ b/core/modules/views/css/views.base-rtl.css
@@ -0,0 +1,3 @@
+.views-exposed-form .views-exposed-widget {
+ float: right; /* RTL */
+}
diff --git a/core/modules/views/css/views.base.css b/core/modules/views/css/views.base.css
new file mode 100644
index 0000000000000000000000000000000000000000..51750a5d420e03b6ba4cf7a3f0b55d8a21e38f58
--- /dev/null
+++ b/core/modules/views/css/views.base.css
@@ -0,0 +1,32 @@
+.views-exposed-form .views-exposed-widget {
+ float: left; /* LTR */
+}
+
+.views-exposed-form .views-exposed-widget .form-submit {
+ margin-top: 0.5em;
+}
+
+.views-exposed-form .form-item,
+.views-exposed-form .form-submit {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.views-exposed-widgets {
+ margin-bottom: 0.5em;
+}
+
+/* table style column align */
+.views-align-left {
+ text-align: left;
+}
+.views-align-right {
+ text-align: right;
+}
+.views-align-center {
+ text-align: center;
+}
+
+.view .progress-disabled {
+ float: none;
+}
diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b19d3114f12ec9fdf5042fbdff85f2f1442bd3fb
--- /dev/null
+++ b/core/modules/views/includes/ajax.inc
@@ -0,0 +1,373 @@
+get('request');
+ $name = $request->request->get('view_name');
+ $display_id = $request->request->get('view_display_id');
+ if (isset($name) && isset($display_id)) {
+ $args = $request->request->get('view_args');
+ $args = isset($args) && $args !== '' ? explode('/', $args) : array();
+ $path = $request->request->get('view_path');
+ $dom_id = $request->request->get('view_dom_id');
+ $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL;
+ $pager_element = $request->request->get('pager_element');
+ $pager_element = isset($pager_element) ? intval($pager_element) : NULL;
+
+ $commands = array();
+
+ // Remove all of this stuff from the query of the request so it doesn't end
+ // up in pagers and tablesort URLs.
+ foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state') as $key) {
+ if ($request->query->has($key)) {
+ $request->query->remove($key);
+ }
+ if ($request->request->has($key)) {
+ $request->request->remove($key);
+ }
+ }
+
+ // Load the view.
+ $view = views_get_view($name);
+ if ($view && $view->access($display_id)) {
+ // Fix the current path for paging.
+ if (!empty($path)) {
+ _current_path($path);
+ }
+
+ // Add all $_POST data, because AJAX is always a post and many things,
+ // such as tablesorts, exposed filters and paging assume $_GET.
+ $request_all = $request->request->all();
+ $query_all = $request->query->all();
+ $request->query->replace($request_all + $query_all);
+
+ // Overwrite the destination.
+ // @see drupal_get_destination()
+ $origin_destination = $path;
+ $query = drupal_http_build_query($request->query->all());
+ if ($query != '') {
+ $origin_destination .= '?' . $query;
+ }
+ $destination = &drupal_static('drupal_get_destination');
+ $destination = array('destination' => $origin_destination);
+
+ // Override the display's pager_element with the one actually used.
+ if (isset($pager_element)) {
+ $commands[] = views_ajax_command_scroll_top('.view-dom-id-' . $dom_id);
+ $view->displayHandlers[$display_id]->setOption('pager_element', $pager_element);
+ }
+ // Reuse the same DOM id so it matches that in Drupal.settings.
+ $view->dom_id = $dom_id;
+
+ $commands[] = ajax_command_replace('.view-dom-id-' . $dom_id, $view->preview($display_id, $args));
+ }
+ drupal_alter('views_ajax_data', $commands, $view);
+ return array('#type' => 'ajax', '#commands' => $commands);
+ }
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsSetForm' command.
+ *
+ * @param $output
+ * The form to display in the modal.
+ * @param $title
+ * The title.
+ * @param $url
+ * An optional URL.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_set_form($output, $title, $url = NULL) {
+ $command = array(
+ 'command' => 'viewsSetForm',
+ 'output' => $output,
+ 'title' => $title,
+ );
+ if (isset($url)) {
+ $command['url'] = $url;
+ }
+ return $command;
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsDismissForm' command.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_dismiss_form() {
+ $command = array(
+ 'command' => 'viewsDismissForm',
+ );
+ return $command;
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsHilite' command.
+ *
+ * @param $selector
+ * The selector to highlight
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_hilite($selector) {
+ return array(
+ 'command' => 'viewsHilite',
+ 'selector' => $selector,
+ );
+}
+
+/**
+ * Creates a Drupal AJAX 'addTab' command.
+ *
+ * @param $id
+ * The DOM ID.
+ * @param $title
+ * The title.
+ * @param $body
+ * The body.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_add_tab($id, $title, $body) {
+ $command = array(
+ 'command' => 'viewsAddTab',
+ 'id' => $id,
+ 'title' => $title,
+ 'body' => $body,
+ );
+ return $command;
+}
+
+/**
+ * Scroll to top of the current view.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_scroll_top($selector) {
+ $command = array(
+ 'command' => 'viewsScrollTop',
+ 'selector' => $selector,
+ );
+ return $command;
+}
+
+/**
+ * Shows Save and Cancel buttons.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_show_buttons() {
+ $command = array(
+ 'command' => 'viewsShowButtons',
+ );
+ return $command;
+}
+
+/**
+ * Trigger the Views live preview.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_trigger_preview() {
+ $command = array(
+ 'command' => 'viewsTriggerPreview',
+ );
+ return $command;
+}
+
+/**
+ * Replace the page title.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_replace_title($title) {
+ $command = array(
+ 'command' => 'viewsReplaceTitle',
+ 'title' => $title,
+ 'siteName' => config('system.site')->get('name'),
+ );
+ return $command;
+}
+
+/**
+ * Return an AJAX error.
+ */
+function views_ajax_error($message) {
+ $commands = array();
+ $commands[] = views_ajax_command_set_form($message, t('Error'));
+ return $commands;
+}
+
+/**
+ * Wrapper around drupal_build_form to handle some AJAX stuff automatically.
+ * This makes some assumptions about the client.
+ */
+function views_ajax_form_wrapper($form_id, &$form_state) {
+ // This won't override settings already in.
+ $form_state += array(
+ 'rerender' => FALSE,
+ 'no_redirect' => !empty($form_state['ajax']),
+ 'no_cache' => TRUE,
+ 'build_info' => array(
+ 'args' => array(),
+ ),
+ );
+
+ $form = drupal_build_form($form_id, $form_state);
+ $output = drupal_render($form);
+
+ // These forms have the title built in, so set the title here:
+ if (empty($form_state['ajax']) && !empty($form_state['title'])) {
+ drupal_set_title($form_state['title']);
+ drupal_add_css(drupal_get_path('module', 'views_ui') . '/css/views-admin.css');
+ }
+
+ if (!empty($form_state['ajax']) && (empty($form_state['executed']) || !empty($form_state['rerender']))) {
+ // If the form didn't execute and we're using ajax, build up a
+ // Ajax command list to execute.
+ $commands = array();
+
+ $display = '';
+ if ($messages = theme('status_messages')) {
+ $display = '
' . t('The following tokens are available. If you would like to have the characters \'[\' and \']\' please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.' . '
'); + foreach (array_keys($options) as $type) { + if (!empty($options[$type])) { + $items = array(); + foreach ($options[$type] as $key => $value) { + $items[] = $key . ' == ' . $value; + } + $output .= theme('item_list', + array( + 'items' => $items, + 'type' => $type + )); + } + } + + $form['token_help'] = array( + '#type' => 'fieldset', + '#title' => t('Replacement patterns'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#value' => $output, + '#id' => 'edit-options-token-help', + '#states' => array( + 'visible' => array( + ':input[name="options[tokenize]"]' => array('checked' => TRUE), + ), + ), + ); + } + } + + public function submitOptionsForm(&$form, &$form_state) { + $form_state['values']['options']['format'] = $form_state['values']['options']['content']['format']; + $form_state['values']['options']['content'] = $form_state['values']['options']['content']['value']; + parent::submitOptionsForm($form, $form_state); + } + + function render($empty = FALSE) { + $format = isset($this->options['format']) ? $this->options['format'] : filter_default_format(); + if (!$empty || !empty($this->options['empty'])) { + return $this->render_textarea($this->options['content'], $format); + } + return ''; + } + + /** + * Render a text area, using the proper format. + */ + function render_textarea($value, $format) { + if ($value) { + if ($this->options['tokenize']) { + $value = $this->view->style_plugin->tokenize_value($value, 0); + } + return check_markup($value, $format, '', FALSE); + } + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php new file mode 100644 index 0000000000000000000000000000000000000000..9e7c172ba99b32a5cf405b7df35d261a4879f814 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php @@ -0,0 +1,62 @@ +options['empty'])) { + return $this->render_textarea_custom($this->options['content']); + } + + return ''; + } + + /** + * Render a text area with filter_xss_admin. + */ + function render_textarea_custom($value) { + if ($value) { + if ($this->options['tokenize']) { + $value = $this->view->style_plugin->tokenize_value($value, 0); + } + return $this->sanitizeValue($value, 'xss_admin'); + } + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php new file mode 100644 index 0000000000000000000000000000000000000000..e9185a009dec458a57d96226ec6ef6cee2ded9b8 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php @@ -0,0 +1,59 @@ + '', 'translatable' => TRUE); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\AreaPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Overridden title'), + '#default_value' => $this->options['title'], + '#description' => t('Override the title of this view when it is empty.'), + ); + + } + + /** + * Overrides Drupal\views\Plugin\views\AreaPluginBase::render(). + */ + function render($empty = FALSE) { + if (!empty($this->options['title'])) { + $this->view->setTitle(filter_xss_admin($this->options['title']), PASS_THROUGH); + } + + return ''; + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php new file mode 100644 index 0000000000000000000000000000000000000000..018996c296085b2ba347d1f74e4de8983b51f544 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php @@ -0,0 +1,92 @@ + ''); + $options['inherit_arguments'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Default options form that provides the label widget that all fields + * should have. + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $view_display = $this->view->storage->name . ':' . $this->view->current_display; + + $options = array('' => t('-Select-')); + $options += views_get_views_as_options(FALSE, 'all', $view_display, FALSE, TRUE); + $form['view_to_insert'] = array( + '#type' => 'select', + '#title' => t('View to insert'), + '#default_value' => $this->options['view_to_insert'], + '#description' => t('The view to insert into this area.'), + '#options' => $options, + ); + + $form['inherit_arguments'] = array( + '#type' => 'checkbox', + '#title' => t('Inherit contextual filters'), + '#default_value' => $this->options['inherit_arguments'], + '#description' => t('If checked, this view will receive the same contextual filters as its parent.'), + ); + } + + /** + * Render the area + */ + function render($empty = FALSE) { + if (!empty($this->options['view_to_insert'])) { + list($view_name, $display_id) = explode(':', $this->options['view_to_insert']); + + $view = views_get_view($view_name); + if (empty($view) || !$view->access($display_id)) { + return; + } + $view->setDisplay($display_id); + + // Avoid recursion + $view->parent_views += $this->view->parent_views; + $view->parent_views[] = "$view_name:$display_id"; + + // Check if the view is part of the parent views of this view + $search = "$view_name:$display_id"; + if (in_array($search, $this->view->parent_views)) { + drupal_set_message(t("Recursion detected in view @view display @display.", array('@view' => $view_name, '@display' => $display_id)), 'error'); + } + else { + if (!empty($this->options['inherit_arguments']) && !empty($this->view->args)) { + return $view->preview($display_id, $this->view->args); + } + else { + return $view->preview($display_id); + } + } + } + return ''; + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php new file mode 100644 index 0000000000000000000000000000000000000000..ef30140fe8cd1d25370105bc8e3fe7aec633ab74 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php @@ -0,0 +1,1121 @@ +definition['name field'])) { + $this->name_field = $this->definition['name field']; + } + if (!empty($this->definition['name table'])) { + $this->name_table = $this->definition['name table']; + } + } + + /** + * Give an argument the opportunity to modify the breadcrumb, if it wants. + * This only gets called on displays where a breadcrumb is actually used. + * + * The breadcrumb will be in the form of an array, with the keys being + * the path and the value being the already sanitized title of the path. + */ + function set_breadcrumb(&$breadcrumb) { } + + /** + * Determine if the argument can generate a breadcrumb + * + * @return TRUE/FALSE + */ + function uses_breadcrumb() { + $info = $this->default_actions($this->options['default_action']); + return !empty($info['breadcrumb']); + } + + function is_exception($arg = NULL) { + if (!isset($arg)) { + $arg = isset($this->argument) ? $this->argument : NULL; + } + return !empty($this->options['exception']['value']) && $this->options['exception']['value'] === $arg; + } + + function exception_title() { + // If title overriding is off for the exception, return the normal title. + if (empty($this->options['exception']['title_enable'])) { + return $this->get_title(); + } + return $this->options['exception']['title']; + } + + /** + * Determine if the argument needs a style plugin. + * + * @return TRUE/FALSE + */ + public function needsStylePlugin() { + $info = $this->default_actions($this->options['default_action']); + $validate_info = $this->default_actions($this->options['validate']['fail']); + return !empty($info['style plugin']) || !empty($validate_info['style plugin']); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['default_action'] = array('default' => 'ignore'); + $options['exception'] = array( + 'contains' => array( + 'value' => array('default' => 'all'), + 'title_enable' => array('default' => FALSE, 'bool' => TRUE), + 'title' => array('default' => 'All', 'translatable' => TRUE), + ), + ); + $options['title_enable'] = array('default' => FALSE, 'bool' => TRUE); + $options['title'] = array('default' => '', 'translatable' => TRUE); + $options['breadcrumb_enable'] = array('default' => FALSE, 'bool' => TRUE); + $options['breadcrumb'] = array('default' => '', 'translatable' => TRUE); + $options['default_argument_type'] = array('default' => 'fixed'); + $options['default_argument_options'] = array('default' => array()); + $options['default_argument_skip_url'] = array('default' => FALSE, 'bool' => TRUE); + $options['summary_options'] = array('default' => array()); + $options['summary'] = array( + 'contains' => array( + 'sort_order' => array('default' => 'asc'), + 'number_of_records' => array('default' => 0), + 'format' => array('default' => 'default_summary'), + ), + ); + $options['specify_validation'] = array('default' => FALSE, 'bool' => TRUE); + $options['validate'] = array( + 'contains' => array( + 'type' => array('default' => 'none'), + 'fail' => array('default' => 'not found'), + ), + ); + $options['validate_options'] = array('default' => array()); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $argument_text = $this->view->display_handler->getArgumentText(); + + $form['#pre_render'][] = 'views_ui_pre_render_move_argument_options'; + + $form['description'] = array( + '#markup' => $argument_text['description'], + '#theme_wrappers' => array('container'), + '#attributes' => array('class' => array('description')), + ); + + $form['no_argument'] = array( + '#type' => 'fieldset', + '#title' => $argument_text['filter value not present'], + ); + // Everything in the fieldset is floated, so the last element needs to + // clear those floats. + $form['no_argument']['clearfix'] = array( + '#weight' => 1000, + '#markup' => '', + ); + $form['default_action'] = array( + '#type' => 'radios', + '#process' => array(array($this, 'processContainerRadios')), + '#default_value' => $this->options['default_action'], + '#fieldset' => 'no_argument', + ); + + $form['exception'] = array( + '#type' => 'fieldset', + '#title' => t('Exceptions'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#fieldset' => 'no_argument', + ); + $form['exception']['value'] = array( + '#type' => 'textfield', + '#title' => t('Exception value'), + '#size' => 20, + '#default_value' => $this->options['exception']['value'], + '#description' => t('If this value is received, the filter will be ignored; i.e, "all values"'), + ); + $form['exception']['title_enable'] = array( + '#type' => 'checkbox', + '#title' => t('Override title'), + '#default_value' => $this->options['exception']['title_enable'], + ); + $form['exception']['title'] = array( + '#type' => 'textfield', + '#title' => t('Override title'), + '#title_display' => 'invisible', + '#size' => 20, + '#default_value' => $this->options['exception']['title'], + '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'), + '#states' => array( + 'visible' => array( + ':input[name="options[exception][title_enable]"]' => array('checked' => TRUE), + ), + ), + ); + + $options = array(); + $defaults = $this->default_actions(); + $validate_options = array(); + foreach ($defaults as $id => $info) { + $options[$id] = $info['title']; + if (empty($info['default only'])) { + $validate_options[$id] = $info['title']; + } + if (!empty($info['form method'])) { + $this->{$info['form method']}($form, $form_state); + } + } + $form['default_action']['#options'] = $options; + + $form['argument_present'] = array( + '#type' => 'fieldset', + '#title' => $argument_text['filter value present'], + ); + $form['title_enable'] = array( + '#type' => 'checkbox', + '#title' => t('Override title'), + '#default_value' => $this->options['title_enable'], + '#fieldset' => 'argument_present', + ); + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Provide title'), + '#title_display' => 'invisible', + '#default_value' => $this->options['title'], + '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'), + '#states' => array( + 'visible' => array( + ':input[name="options[title_enable]"]' => array('checked' => TRUE), + ), + ), + '#fieldset' => 'argument_present', + ); + + $form['breadcrumb_enable'] = array( + '#type' => 'checkbox', + '#title' => t('Override breadcrumb'), + '#default_value' => $this->options['breadcrumb_enable'], + '#fieldset' => 'argument_present', + ); + $form['breadcrumb'] = array( + '#type' => 'textfield', + '#title' => t('Provide breadcrumb'), + '#title_display' => 'invisible', + '#default_value' => $this->options['breadcrumb'], + '#description' => t('Enter a breadcrumb name you would like to use. See "Title" for percent substitutions.'), + '#states' => array( + 'visible' => array( + ':input[name="options[breadcrumb_enable]"]' => array('checked' => TRUE), + ), + ), + '#fieldset' => 'argument_present', + ); + + $form['specify_validation'] = array( + '#type' => 'checkbox', + '#title' => t('Specify validation criteria'), + '#default_value' => $this->options['specify_validation'], + '#fieldset' => 'argument_present', + ); + + $form['validate'] = array( + '#type' => 'container', + '#fieldset' => 'argument_present', + ); + $form['validate']['type'] = array( + '#type' => 'select', + '#title' => t('Validator'), + '#default_value' => $this->options['validate']['type'], + '#states' => array( + 'visible' => array( + ':input[name="options[specify_validation]"]' => array('checked' => TRUE), + ), + ), + ); + + $plugins = views_get_plugin_definitions('argument_validator'); + foreach ($plugins as $id => $info) { + if (!empty($info['no_ui'])) { + continue; + } + + $valid = TRUE; + if (!empty($info['type'])) { + $valid = FALSE; + if (empty($this->definition['validate type'])) { + continue; + } + foreach ((array) $info['type'] as $type) { + if ($type == $this->definition['validate type']) { + $valid = TRUE; + break; + } + } + } + + // If we decide this validator is ok, add it to the list. + if ($valid) { + $plugin = $this->get_plugin('argument_validator', $id); + if ($plugin) { + if ($plugin->access() || $this->options['validate']['type'] == $id) { + $form['validate']['options'][$id] = array( + '#prefix' => 'The following tokens are available for this link.
'); + foreach (array_keys($options) as $type) { + if (!empty($options[$type])) { + $items = array(); + foreach ($options[$type] as $key => $value) { + $items[] = $key . ' == ' . $value; + } + $output .= theme('item_list', + array( + 'items' => $items, + 'type' => $type + )); + } + } + } + + $form['link_url'] = array( + '#type' => 'textfield', + '#title' => t('Custom URL'), + '#default_value' => $this->getOption('link_url'), + '#description' => t('A Drupal path or external URL the more link will point to. Note that this will override the link display setting above.') . $output, + '#states' => array( + 'visible' => array( + ':input[name="link_display"]' => array('value' => 'custom_url'), + ), + ), + ); + break; + case 'analyze-theme': + $form['#title'] .= t('Theming information'); + if ($theme = drupal_container()->get('request')->request->get('theme')) { + $this->theme = $theme; + } + elseif (empty($this->theme)) { + $this->theme = variable_get('theme_default', 'bartik'); + } + + if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) { + $this->theme_registry = theme_get_registry(); + $theme_engine = $GLOBALS['theme_engine']; + } + else { + $themes = list_themes(); + $theme = $themes[$this->theme]; + + // Find all our ancestor themes and put them in an array. + $base_theme = array(); + $ancestor = $this->theme; + while ($ancestor && isset($themes[$ancestor]->base_theme)) { + $ancestor = $themes[$ancestor]->base_theme; + $base_theme[] = $themes[$ancestor]; + } + + // The base themes should be initialized in the right order. + $base_theme = array_reverse($base_theme); + + // This code is copied directly from _drupal_theme_initialize() + $theme_engine = NULL; + + // Initialize the theme. + if (isset($theme->engine)) { + // Include the engine. + include_once DRUPAL_ROOT . '/' . $theme->owner; + + $theme_engine = $theme->engine; + if (function_exists($theme_engine . '_init')) { + foreach ($base_theme as $base) { + call_user_func($theme_engine . '_init', $base); + } + call_user_func($theme_engine . '_init', $theme); + } + } + else { + // include non-engine theme files + foreach ($base_theme as $base) { + // Include the theme file or the engine. + if (!empty($base->owner)) { + include_once DRUPAL_ROOT . '/' . $base->owner; + } + } + // and our theme gets one too. + if (!empty($theme->owner)) { + include_once DRUPAL_ROOT . '/' . $theme->owner; + } + } + $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine); + } + + // If there's a theme engine involved, we also need to know its extension + // so we can give the proper filename. + $this->theme_extension = '.tpl.php'; + if (isset($theme_engine)) { + $extension_function = $theme_engine . '_extension'; + if (function_exists($extension_function)) { + $this->theme_extension = $extension_function(); + } + } + + $funcs = array(); + // Get theme functions for the display. Note that some displays may + // not have themes. The 'feed' display, for example, completely + // delegates to the style. + if (!empty($this->definition['theme'])) { + $funcs[] = $this->optionLink(t('Display output'), 'analyze-theme-display') . ': ' . $this->formatThemes($this->themeFunctions()); + $themes = $this->additionalThemeFunctions(); + if ($themes) { + foreach ($themes as $theme) { + $funcs[] = $this->optionLink(t('Alternative display output'), 'analyze-theme-display') . ': ' . $this->formatThemes($theme); + } + } + } + + $plugin = $this->getPlugin('style'); + if ($plugin) { + $funcs[] = $this->optionLink(t('Style output'), 'analyze-theme-style') . ': ' . $this->formatThemes($plugin->themeFunctions(), $plugin->additionalThemeFunctions()); + $themes = $plugin->additionalThemeFunctions(); + if ($themes) { + foreach ($themes as $theme) { + $funcs[] = $this->optionLink(t('Alternative style'), 'analyze-theme-style') . ': ' . $this->formatThemes($theme); + } + } + + if ($plugin->usesRowPlugin()) { + $row_plugin = $this->getPlugin('row'); + if ($row_plugin) { + $funcs[] = $this->optionLink(t('Row style output'), 'analyze-theme-row') . ': ' . $this->formatThemes($row_plugin->themeFunctions()); + $themes = $row_plugin->additionalThemeFunctions(); + if ($themes) { + foreach ($themes as $theme) { + $funcs[] = $this->optionLink(t('Alternative row style'), 'analyze-theme-row') . ': ' . $this->formatThemes($theme); + } + } + } + } + + if ($plugin->usesFields()) { + foreach ($this->getHandlers('field') as $id => $handler) { + $funcs[] = $this->optionLink(t('Field @field (ID: @id)', array('@field' => $handler->adminLabel(), '@id' => $id)), 'analyze-theme-field') . ': ' . $this->formatThemes($handler->themeFunctions()); + } + } + } + + $form['important'] = array( + '#markup' => '' . t('This section lists all possible templates for the display plugin and for the style plugins, ordered roughly from the least specific to the most specific. The active template for each plugin -- which is the most specific template found on the system -- is highlighted in bold.') . '
' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '
'; + + if (empty($this->definition['theme'])) { + $output .= t('This display has no theming information'); + } + else { + $output .= '' . t('This is the default theme template used for this display.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . ''; + } + + if (!empty($this->definition['additional themes'])) { + foreach ($this->definition['additional themes'] as $theme => $type) { + $output .= '
' . t('This is an alternative template for this display.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . ''; + } + } + + $form['analysis'] = array( + '#markup' => '
' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '
'; + + $plugin = $this->getPlugin('style'); + + if (empty($plugin->definition['theme'])) { + $output .= t('This display has no style theming information'); + } + else { + $output .= '' . t('This is the default theme template used for this style.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . ''; + } + + if (!empty($plugin->definition['additional themes'])) { + foreach ($plugin->definition['additional themes'] as $theme => $type) { + $output .= '
' . t('This is an alternative template for this style.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . ''; + } + } + + $form['analysis'] = array( + '#markup' => '
' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '
'; + + $plugin = $this->getPlugin('row'); + + if (empty($plugin->definition['theme'])) { + $output .= t('This display has no row style theming information'); + } + else { + $output .= '' . t('This is the default theme template used for this row style.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . ''; + } + + if (!empty($plugin->definition['additional themes'])) { + foreach ($plugin->definition['additional themes'] as $theme => $type) { + $output .= '
' . t('This is an alternative template for this row style.') . '
'; + $output .= '' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . ''; + } + } + + $form['analysis'] = array( + '#markup' => '
' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '
'; + + $output .= '' . t('This is the default theme template used for this row style.') . '
'; + + // Field templates aren't registered the normal way...and they're always + // this one, anyhow. + $output .= '' . check_plain(file_get_contents(drupal_get_path('module', 'views') . '/theme/views-view-field.tpl.php')) . ''; + + $form['analysis'] = array( + '#markup' => '
' . check_plain($this->view->render()) . ''; + } + return $this->view->render(); + } + + /** + * Instead of going through the standard views_view.tpl.php, delegate this + * to the style handler. + */ + public function render() { + return $this->view->style_plugin->render($this->view->result); + } + + public function defaultableSections($section = NULL) { + if (in_array($section, array('style', 'row'))) { + return FALSE; + } + + $sections = parent::defaultableSections($section); + + // Tell views our sitename_title option belongs in the title section. + if ($section == 'title') { + $sections[] = 'sitename_title'; + } + elseif (!$section) { + $sections['title'][] = 'sitename_title'; + } + return $sections; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['displays'] = array('default' => array()); + + // Overrides for standard stuff: + $options['style']['contains']['type']['default'] = 'rss'; + $options['style']['contains']['options']['default'] = array('description' => ''); + $options['sitename_title']['default'] = FALSE; + $options['row']['contains']['type']['default'] = ''; + $options['defaults']['default']['style'] = FALSE; + $options['defaults']['default']['row'] = FALSE; + + return $options; + } + + public function optionsSummary(&$categories, &$options) { + // It is very important to call the parent function here: + parent::optionsSummary($categories, $options); + + // Since we're childing off the 'page' type, we'll still *call* our + // category 'page' but let's override it so it says feed settings. + $categories['page'] = array( + 'title' => t('Feed settings'), + 'column' => 'second', + 'build' => array( + '#weight' => -10, + ), + ); + + if ($this->getOption('sitename_title')) { + $options['title']['value'] = t('Using the site name'); + } + + // I don't think we want to give feeds menus directly. + unset($options['menu']); + + $displays = array_filter($this->getOption('displays')); + if (count($displays) > 1) { + $attach_to = t('Multiple displays'); + } + elseif (count($displays) == 1) { + $display = array_shift($displays); + if (!empty($this->view->storage->display[$display])) { + $attach_to = check_plain($this->view->storage->display[$display]['display_title']); + } + } + + if (!isset($attach_to)) { + $attach_to = t('None'); + } + + $options['displays'] = array( + 'category' => 'page', + 'title' => t('Attach to'), + 'value' => $attach_to, + ); + } + + /** + * Provide the default form for setting options. + */ + public function buildOptionsForm(&$form, &$form_state) { + // It is very important to call the parent function here. + parent::buildOptionsForm($form, $form_state); + + switch ($form_state['section']) { + case 'title': + $title = $form['title']; + // A little juggling to move the 'title' field beyond our checkbox. + unset($form['title']); + $form['sitename_title'] = array( + '#type' => 'checkbox', + '#title' => t('Use the site name for the title'), + '#default_value' => $this->getOption('sitename_title'), + ); + $form['title'] = $title; + $form['title']['#states'] = array( + 'visible' => array( + ':input[name="sitename_title"]' => array('checked' => FALSE), + ), + ); + break; + case 'displays': + $form['#title'] .= t('Attach to'); + $displays = array(); + foreach ($this->view->storage->display as $display_id => $display) { + // @todo The display plugin should have display_title and id as well. + if (!empty($this->view->displayHandlers[$display_id]) && $this->view->displayHandlers[$display_id]->acceptAttachments()) { + $displays[$display_id] = $display['display_title']; + } + } + $form['displays'] = array( + '#type' => 'checkboxes', + '#description' => t('The feed icon will be available only to the selected displays.'), + '#options' => $displays, + '#default_value' => $this->getOption('displays'), + ); + break; + case 'path': + $form['path']['#description'] = t('This view will be displayed by visiting this path on your site. It is recommended that the path be something like "path/%/%/feed" or "path/%/%/rss.xml", putting one % in the path for each contextual filter you have defined in the view.'); + } + } + + /** + * Perform any necessary changes to the form values prior to storage. + * There is no need for this function to actually store the data. + */ + public function submitOptionsForm(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::submitOptionsForm($form, $form_state); + switch ($form_state['section']) { + case 'title': + $this->setOption('sitename_title', $form_state['values']['sitename_title']); + break; + case 'displays': + $this->setOption($form_state['section'], $form_state['values'][$form_state['section']]); + break; + } + } + + /** + * Attach to another view. + */ + public function attachTo($display_id) { + $displays = $this->getOption('displays'); + if (empty($displays[$display_id])) { + return; + } + + // Defer to the feed style; it may put in meta information, and/or + // attach a feed icon. + $plugin = $this->getPlugin('style'); + if ($plugin) { + $clone = $this->view->cloneView(); + $clone->setDisplay($this->display['id']); + $clone->buildTitle(); + $plugin->attach_to($display_id, $this->getPath(), $clone->getTitle()); + + // Clean up + $clone->destroy(); + unset($clone); + } + } + + public function usesLinkDisplay() { + return TRUE; + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php new file mode 100644 index 0000000000000000000000000000000000000000..9bc4346d7da6e699db498e5b6f8fcf05ef7a0e95 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php @@ -0,0 +1,654 @@ + ''); + $options['menu'] = array( + 'contains' => array( + 'type' => array('default' => 'none'), + // Do not translate menu and title as menu system will. + 'title' => array('default' => '', 'translatable' => FALSE), + 'description' => array('default' => '', 'translatable' => FALSE), + 'weight' => array('default' => 0), + 'name' => array('default' => variable_get('menu_default_node_menu', 'navigation')), + 'context' => array('default' => ''), + ), + ); + $options['tab_options'] = array( + 'contains' => array( + 'type' => array('default' => 'none'), + // Do not translate menu and title as menu system will. + 'title' => array('default' => '', 'translatable' => FALSE), + 'description' => array('default' => '', 'translatable' => FALSE), + 'weight' => array('default' => 0), + 'name' => array('default' => 'navigation'), + ), + ); + + return $options; + } + + /** + * Add this display's path information to Drupal's menu system. + */ + public function executeHookMenu($callbacks) { + $items = array(); + // Replace % with the link to our standard views argument loader + // views_arg_load -- which lives in views.module + + $bits = explode('/', $this->getOption('path')); + $page_arguments = array($this->view->storage->name, $this->display['id']); + $this->view->initHandlers(); + $view_arguments = $this->view->argument; + + // Replace % with %views_arg for menu autoloading and add to the + // page arguments so the argument actually comes through. + foreach ($bits as $pos => $bit) { + if ($bit == '%') { + $argument = array_shift($view_arguments); + if (!empty($argument->options['specify_validation']) && $argument->options['validate']['type'] != 'none') { + $bits[$pos] = '%views_arg'; + } + $page_arguments[] = $pos; + } + } + + $path = implode('/', $bits); + + $access_plugin = $this->getPlugin('access'); + if (!isset($access_plugin)) { + $access_plugin = views_get_plugin('access', 'none'); + } + + // Get access callback might return an array of the callback + the dynamic arguments. + $access_plugin_callback = $access_plugin->get_access_callback(); + + if (is_array($access_plugin_callback)) { + $access_arguments = array(); + + // Find the plugin arguments. + $access_plugin_method = array_shift($access_plugin_callback); + $access_plugin_arguments = array_shift($access_plugin_callback); + if (!is_array($access_plugin_arguments)) { + $access_plugin_arguments = array(); + } + + $access_arguments[0] = array($access_plugin_method, &$access_plugin_arguments); + + // Move the plugin arguments to the access arguments array. + $i = 1; + foreach ($access_plugin_arguments as $key => $value) { + if (is_int($value)) { + $access_arguments[$i] = $value; + $access_plugin_arguments[$key] = $i; + $i++; + } + } + } + else { + $access_arguments = array($access_plugin_callback); + } + + if ($path) { + $items[$path] = array( + // default views page entry + 'page callback' => 'views_page', + 'page arguments' => $page_arguments, + // Default access check (per display) + 'access callback' => 'views_access', + 'access arguments' => $access_arguments, + // Identify URL embedded arguments and correlate them to a handler + 'load arguments' => array($this->view->storage->name, $this->display['id'], '%index'), + ); + $menu = $this->getOption('menu'); + if (empty($menu)) { + $menu = array('type' => 'none'); + } + // Set the title and description if we have one. + if ($menu['type'] != 'none') { + $items[$path]['title'] = $menu['title']; + $items[$path]['description'] = $menu['description']; + } + + if (isset($menu['weight'])) { + $items[$path]['weight'] = intval($menu['weight']); + } + + switch ($menu['type']) { + case 'none': + default: + $items[$path]['type'] = MENU_CALLBACK; + break; + case 'normal': + $items[$path]['type'] = MENU_NORMAL_ITEM; + // Insert item into the proper menu + $items[$path]['menu_name'] = $menu['name']; + break; + case 'tab': + $items[$path]['type'] = MENU_LOCAL_TASK; + break; + case 'default tab': + $items[$path]['type'] = MENU_DEFAULT_LOCAL_TASK; + break; + } + + // Add context for contextual links. + // @see menu_contextual_links() + if (!empty($menu['context'])) { + $items[$path]['context'] = MENU_CONTEXT_INLINE; + } + + // If this is a 'default' tab, check to see if we have to create teh + // parent menu item. + if ($menu['type'] == 'default tab') { + $tab_options = $this->getOption('tab_options'); + if (!empty($tab_options['type']) && $tab_options['type'] != 'none') { + $bits = explode('/', $path); + // Remove the last piece. + $bit = array_pop($bits); + + // we can't do this if they tried to make the last path bit variable. + // @todo: We can validate this. + if ($bit != '%views_arg' && !empty($bits)) { + $default_path = implode('/', $bits); + $items[$default_path] = array( + // default views page entry + 'page callback' => 'views_page', + 'page arguments' => $page_arguments, + // Default access check (per display) + 'access callback' => 'views_access', + 'access arguments' => $access_arguments, + // Identify URL embedded arguments and correlate them to a handler + 'load arguments' => array($this->view->storage->name, $this->display['id'], '%index'), + 'title' => $tab_options['title'], + 'description' => $tab_options['description'], + 'menu_name' => $tab_options['name'], + ); + switch ($tab_options['type']) { + default: + case 'normal': + $items[$default_path]['type'] = MENU_NORMAL_ITEM; + break; + case 'tab': + $items[$default_path]['type'] = MENU_LOCAL_TASK; + break; + } + if (isset($tab_options['weight'])) { + $items[$default_path]['weight'] = intval($tab_options['weight']); + } + } + } + } + } + + return $items; + } + + /** + * The display page handler returns a normal view, but it also does + * a drupal_set_title for the page, and does a views_set_page_view + * on the view. + */ + public function execute() { + // Let the world know that this is the page view we're using. + views_set_page_view($this->view); + + // Prior to this being called, the $view should already be set to this + // display, and arguments should be set on the view. + $this->view->build(); + if (!empty($this->view->build_info['fail'])) { + throw new NotFoundHttpException(); + } + + if (!empty($this->view->build_info['denied'])) { + throw new AccessDeniedHttpException(); + } + + $this->view->getBreadcrumb(TRUE); + + + // And now render the view. + $render = $this->view->render(); + + // First execute the view so it's possible to get tokens for the title. + // And the title, which is much easier. + drupal_set_title(filter_xss_admin($this->view->getTitle()), PASS_THROUGH); + return $render; + } + + /** + * Provide the summary for page options in the views UI. + * + * This output is returned as an array. + */ + public function optionsSummary(&$categories, &$options) { + // It is very important to call the parent function here: + parent::optionsSummary($categories, $options); + + $categories['page'] = array( + 'title' => t('Page settings'), + 'column' => 'second', + 'build' => array( + '#weight' => -10, + ), + ); + + $path = strip_tags('/' . $this->getOption('path')); + if (empty($path)) { + $path = t('None'); + } + + $options['path'] = array( + 'category' => 'page', + 'title' => t('Path'), + 'value' => views_ui_truncate($path, 24), + ); + + $menu = $this->getOption('menu'); + if (!is_array($menu)) { + $menu = array('type' => 'none'); + } + switch ($menu['type']) { + case 'none': + default: + $menu_str = t('No menu'); + break; + case 'normal': + $menu_str = t('Normal: @title', array('@title' => $menu['title'])); + break; + case 'tab': + case 'default tab': + $menu_str = t('Tab: @title', array('@title' => $menu['title'])); + break; + } + + $options['menu'] = array( + 'category' => 'page', + 'title' => t('Menu'), + 'value' => views_ui_truncate($menu_str, 24), + ); + + // This adds a 'Settings' link to the style_options setting if the style has options. + if ($menu['type'] == 'default tab') { + $options['menu']['setting'] = t('Parent menu item'); + $options['menu']['links']['tab_options'] = t('Change settings for the parent menu'); + } + } + + /** + * Provide the default form for setting options. + */ + public function buildOptionsForm(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::buildOptionsForm($form, $form_state); + + switch ($form_state['section']) { + case 'path': + $form['#title'] .= t('The menu path or URL of this view'); + $form['path'] = array( + '#type' => 'textfield', + '#description' => t('This view will be displayed by visiting this path on your site. You may use "%" in your URL to represent values that will be used for contextual filters: For example, "node/%/feed".'), + '#default_value' => $this->getOption('path'), + '#field_prefix' => '' . url(NULL, array('absolute' => TRUE)), + '#field_suffix' => '', + '#attributes' => array('dir' => 'ltr'), + ); + break; + case 'menu': + $form['#title'] .= t('Menu item entry'); + $form['menu'] = array( + '#prefix' => '
You must add some additional fields to this display before using this field. These fields may be marked as Exclude from display if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.
'); + // We have some options, so make a list. + if (!empty($options)) { + $output = t('The following tokens are available for this field. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields. +If you would like to have the characters \'[\' and \']\' please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.
'); + foreach (array_keys($options) as $type) { + if (!empty($options[$type])) { + $items = array(); + foreach ($options[$type] as $key => $value) { + $items[] = $key . ' == ' . $value; + } + $output .= theme('item_list', + array( + 'items' => $items, + 'type' => $type + )); + } + } + } + // This construct uses 'hidden' and not markup because process doesn't + // run. It also has an extra div because the dependency wants to hide + // the parent in situations like this, so we need a second div to + // make this work. + $form['alter']['help'] = array( + '#type' => 'fieldset', + '#title' => t('Replacement patterns'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#value' => $output, + '#states' => array( + 'visible' => array( + array( + ':input[name="options[alter][make_link]"]' => array('checked' => TRUE), + ), + array( + ':input[name="options[alter][alter_text]"]' => array('checked' => TRUE), + ), + array( + ':input[name="options[alter][more_link]"]' => array('checked' => TRUE), + ), + ), + ), + ); + + $form['alter']['trim'] = array( + '#type' => 'checkbox', + '#title' => t('Trim this field to a maximum length'), + '#description' => t('Enable to trim the field to a maximum length of characters'), + '#default_value' => $this->options['alter']['trim'], + ); + + $form['alter']['max_length'] = array( + '#title' => t('Maximum length'), + '#type' => 'textfield', + '#default_value' => $this->options['alter']['max_length'], + '#description' => t('The maximum number of characters this field can be.'), + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['word_boundary'] = array( + '#type' => 'checkbox', + '#title' => t('Trim only on a word boundary'), + '#description' => t('If checked, this field be trimmed only on a word boundary. This is guaranteed to be the maximum characters stated or less. If there are no word boundaries this could trim a field to nothing.'), + '#default_value' => $this->options['alter']['word_boundary'], + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['ellipsis'] = array( + '#type' => 'checkbox', + '#title' => t('Add an ellipsis'), + '#description' => t('If checked, a "..." will be added if a field was trimmed.'), + '#default_value' => $this->options['alter']['ellipsis'], + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['more_link'] = array( + '#type' => 'checkbox', + '#title' => t('Add a read-more link if output is trimmed.'), + '#description' => t('If checked, a read-more link will be added at the end of the trimmed output'), + '#default_value' => $this->options['alter']['more_link'], + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['more_link_text'] = array( + '#type' => 'textfield', + '#title' => t('More link text'), + '#default_value' => $this->options['alter']['more_link_text'], + '#description' => t('The text which will be displayed on the more link. You may enter data from this view as per the "Replacement patterns" above.'), + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ':input[name="options[alter][more_link]"]' => array('checked' => TRUE), + ), + ), + ); + $form['alter']['more_link_path'] = array( + '#type' => 'textfield', + '#title' => t('More link path'), + '#default_value' => $this->options['alter']['more_link_path'], + '#description' => t('The path which is used for the more link. You may enter data from this view as per the "Replacement patterns" above.'), + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ':input[name="options[alter][more_link]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['html'] = array( + '#type' => 'checkbox', + '#title' => t('Field can contain HTML'), + '#description' => t('If checked, HTML corrector will be run to ensure tags are properly closed after trimming.'), + '#default_value' => $this->options['alter']['html'], + '#states' => array( + 'visible' => array( + ':input[name="options[alter][trim]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['strip_tags'] = array( + '#type' => 'checkbox', + '#title' => t('Strip HTML tags'), + '#description' => t('If checked, all HTML tags will be stripped.'), + '#default_value' => $this->options['alter']['strip_tags'], + ); + + $form['alter']['preserve_tags'] = array( + '#type' => 'textfield', + '#title' => t('Preserve certain tags'), + '#description' => t('List the tags that need to be preserved during the stripping process. example "<p> <br>" which will preserve all p and br elements'), + '#default_value' => $this->options['alter']['preserve_tags'], + '#states' => array( + 'visible' => array( + ':input[name="options[alter][strip_tags]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['alter']['trim_whitespace'] = array( + '#type' => 'checkbox', + '#title' => t('Remove whitespace'), + '#description' => t('If checked, all whitespaces at the beginning and the end of the output will be removed.'), + '#default_value' => $this->options['alter']['trim_whitespace'], + ); + + $form['alter']['nl2br'] = array( + '#type' => 'checkbox', + '#title' => t('Convert newlines to HTML <br> tags'), + '#description' => t('If checked, all newlines chars (e.g. \n) are converted into HTML <br> tags.'), + '#default_value' => $this->options['alter']['nl2br'], + ); + } + + $form['empty_field_behavior'] = array( + '#type' => 'fieldset', + '#title' => t('No results behavior'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => 100, + ); + + $form['empty'] = array( + '#type' => 'textarea', + '#title' => t('No results text'), + '#default_value' => $this->options['empty'], + '#description' => t('Provide text to display if this field contains an empty result. You may include HTML. You may enter data from this view as per the "Replacement patterns" in the "Rewrite Results" section below.'), + '#fieldset' => 'empty_field_behavior', + ); + + $form['empty_zero'] = array( + '#type' => 'checkbox', + '#title' => t('Count the number 0 as empty'), + '#default_value' => $this->options['empty_zero'], + '#description' => t('Enable to display the "no results text" if the field contains the number 0.'), + '#fieldset' => 'empty_field_behavior', + ); + + $form['hide_empty'] = array( + '#type' => 'checkbox', + '#title' => t('Hide if empty'), + '#default_value' => $this->options['hide_empty'], + '#description' => t('Enable to hide this field if it is empty. Note that the field label or rewritten output may still be displayed. To hide labels, check the style or row style settings for empty fields. To hide rewritten content, check the "Hide rewriting if empty" checkbox.'), + '#fieldset' => 'empty_field_behavior', + ); + + $form['hide_alter_empty'] = array( + '#type' => 'checkbox', + '#title' => t('Hide rewriting if empty'), + '#default_value' => $this->options['hide_alter_empty'], + '#description' => t('Do not display rewritten content if this field is empty.'), + '#fieldset' => 'empty_field_behavior', + ); + } + + /** + * Provide extra data to the administration form + */ + public function adminSummary() { + return $this->label(); + } + + /** + * Run before any fields are rendered. + * + * This gives the handlers some time to set up before any handler has + * been rendered. + * + * @param $values + * An array of all objects returned from the query. + */ + function pre_render(&$values) { } + + /** + * Render the field. + * + * @param $values + * The values retrieved from the database. + */ + function render($values) { + $value = $this->get_value($values); + return $this->sanitizeValue($value); + } + + /** + * Render a field using advanced settings. + * + * This renders a field normally, then decides if render-as-link and + * text-replacement rendering is necessary. + */ + function advanced_render($values) { + if ($this->allow_advanced_render() && method_exists($this, 'render_item')) { + $raw_items = $this->get_items($values); + // If there are no items, set the original value to NULL. + if (empty($raw_items)) { + $this->original_value = NULL; + } + } + else { + $value = $this->render($values); + if (is_array($value)) { + $value = drupal_render($value); + } + $this->last_render = $value; + $this->original_value = $value; + } + + if ($this->allow_advanced_render()) { + $tokens = NULL; + if (method_exists($this, 'render_item')) { + $items = array(); + foreach ($raw_items as $count => $item) { + $value = $this->render_item($count, $item); + if (is_array($value)) { + $value = drupal_render($value); + } + $this->last_render = $value; + $this->original_value = $this->last_render; + + $alter = $item + $this->options['alter']; + $alter['phase'] = VIEWS_HANDLER_RENDER_TEXT_PHASE_SINGLE_ITEM; + $items[] = $this->render_text($alter); + } + + $value = $this->render_items($items); + } + else { + $alter = array('phase' => VIEWS_HANDLER_RENDER_TEXT_PHASE_COMPLETELY) + $this->options['alter']; + $value = $this->render_text($alter); + } + + if (is_array($value)) { + $value = drupal_render($value); + } + // This happens here so that render_as_link can get the unaltered value of + // this field as a token rather than the altered value. + $this->last_render = $value; + } + + if (empty($this->last_render)) { + if ($this->is_value_empty($this->last_render, $this->options['empty_zero'], FALSE)) { + $alter = $this->options['alter']; + $alter['alter_text'] = 1; + $alter['text'] = $this->options['empty']; + $alter['phase'] = VIEWS_HANDLER_RENDER_TEXT_PHASE_EMPTY; + $this->last_render = $this->render_text($alter); + } + } + + return $this->last_render; + } + + /** + * Checks if a field value is empty. + * + * @param $value + * The field value. + * @param bool $empty_zero + * Whether or not this field is configured to consider 0 as empty. + * @param bool $no_skip_empty + * Whether or not to use empty() to check the value. + * + * @return bool + * TRUE if the value is considered empty, FALSE otherwise. + */ + function is_value_empty($value, $empty_zero, $no_skip_empty = TRUE) { + if (!isset($value)) { + $empty = TRUE; + } + else { + $empty = ($empty_zero || ($value !== 0 && $value !== '0')); + } + + if ($no_skip_empty) { + $empty = empty($value) && $empty; + } + return $empty; + } + + /** + * Perform an advanced text render for the item. + * + * This is separated out as some fields may render lists, and this allows + * each item to be handled individually. + */ + function render_text($alter) { + $value = $this->last_render; + + if (!empty($alter['alter_text']) && $alter['text'] !== '') { + $tokens = $this->get_render_tokens($alter); + $value = $this->render_altered($alter, $tokens); + } + + if (!empty($this->options['alter']['trim_whitespace'])) { + $value = trim($value); + } + + // Check if there should be no further rewrite for empty values. + $no_rewrite_for_empty = $this->options['hide_alter_empty'] && $this->is_value_empty($this->original_value, $this->options['empty_zero']); + + // Check whether the value is empty and return nothing, so the field isn't rendered. + // First check whether the field should be hidden if the value(hide_alter_empty = TRUE) /the rewrite is empty (hide_alter_empty = FALSE). + // For numeric values you can specify whether "0"/0 should be empty. + if ((($this->options['hide_empty'] && empty($value)) + || ($alter['phase'] != VIEWS_HANDLER_RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty)) + && $this->is_value_empty($value, $this->options['empty_zero'], FALSE)) { + return ''; + } + // Only in empty phase. + if ($alter['phase'] == VIEWS_HANDLER_RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty) { + // If we got here then $alter contains the value of "No results text" + // and so there is nothing left to do. + return $value; + } + + if (!empty($alter['strip_tags'])) { + $value = strip_tags($value, $alter['preserve_tags']); + } + + $suffix = ''; + if (!empty($alter['trim']) && !empty($alter['max_length'])) { + $length = strlen($value); + $value = $this->render_trim_text($alter, $value); + if ($this->options['alter']['more_link'] && strlen($value) < $length) { + $tokens = $this->get_render_tokens($alter); + $more_link_text = $this->options['alter']['more_link_text'] ? $this->options['alter']['more_link_text'] : t('more'); + $more_link_text = strtr(filter_xss_admin($more_link_text), $tokens); + $more_link_path = $this->options['alter']['more_link_path']; + $more_link_path = strip_tags(decode_entities(strtr($more_link_path, $tokens))); + + // Take sure that paths which was runned through url() does work as well. + $base_path = base_path(); + // Checks whether the path starts with the base_path. + if (strpos($more_link_path, $base_path) === 0) { + $more_link_path = drupal_substr($more_link_path, drupal_strlen($base_path)); + } + + $more_link = l($more_link_text, $more_link_path, array('attributes' => array('class' => array('views-more-link')))); + + $suffix .= " " . $more_link; + } + } + + if (!empty($alter['nl2br'])) { + $value = nl2br($value); + } + $this->last_render_text = $value; + + if (!empty($alter['make_link']) && !empty($alter['path'])) { + if (!isset($tokens)) { + $tokens = $this->get_render_tokens($alter); + } + $value = $this->render_as_link($alter, $value, $tokens); + } + + return $value . $suffix; + } + + /** + * Render this field as altered text, from a fieldset set by the user. + */ + function render_altered($alter, $tokens) { + // Filter this right away as our substitutions are already sanitized. + $value = filter_xss_admin($alter['text']); + $value = strtr($value, $tokens); + + return $value; + } + + /** + * Trim the field down to the specified length. + */ + function render_trim_text($alter, $value) { + if (!empty($alter['strip_tags'])) { + // NOTE: It's possible that some external fields might override the + // element type. + $this->definition['element type'] = 'span'; + } + return views_trim_text($alter, $value); + } + + /** + * Render this field as a link, with the info from a fieldset set by + * the user. + */ + function render_as_link($alter, $text, $tokens) { + $value = ''; + + if (!empty($alter['prefix'])) { + $value .= filter_xss_admin(strtr($alter['prefix'], $tokens)); + } + + $options = array( + 'html' => TRUE, + 'absolute' => !empty($alter['absolute']) ? TRUE : FALSE, + ); + + // $path will be run through check_url() by l() so we do not need to + // sanitize it ourselves. + $path = $alter['path']; + + // strip_tags() removesReturned data set: ' . print_r($result, TRUE) . "\n\nExpected: ". print_r($expected_result, TRUE)); + + // Do the actual comparison. + return $this->$assert_method($result, $expected_result, $message); + } + + /** + * Helper function: order an array of array based on a column. + */ + protected function orderResultSet($result_set, $column, $reverse = FALSE) { + $this->sort_column = $column; + $this->sort_order = $reverse ? -1 : 1; + usort($result_set, array($this, 'helperCompareFunction')); + return $result_set; + } + + protected $sort_column = NULL; + protected $sort_order = 1; + + /** + * Helper comparison function for orderResultSet(). + */ + protected function helperCompareFunction($a, $b) { + $value1 = $a[$this->sort_column]; + $value2 = $b[$this->sort_column]; + if ($value1 == $value2) { + return 0; + } + return $this->sort_order * (($value1 < $value2) ? -1 : 1); + } + + /** + * Helper function to check whether a button with a certain id exists and has a certain label. + */ + protected function helperButtonHasLabel($id, $expected_label, $message = 'Label has the expected value: %label.') { + return $this->assertFieldById($id, $expected_label, t($message, array('%label' => $expected_label))); + } + + /** + * Helper function to execute a view with debugging. + * + * @param view $view + * @param array $args + */ + protected function executeView($view, $args = array()) { + $view->setDisplay(); + $view->preExecute($args); + $view->execute(); + $this->verbose('Executed view: ' . ((string) $view->build_info['query']) . ''); + } + + /** + * Build and return a Page view of the views_test_data table. + * + * @return view + */ + protected function getBasicPageView() { + $view = $this->getView(); + + // In order to test exposed filters, we have to disable + // the exposed forms cache. + drupal_static_reset('views_exposed_form_cache'); + + $display = $view->storage->newDisplay('page', 'Page', 'page_1'); + return $view; + } + + /** + * The schema definition. + */ + protected function schemaDefinition() { + $schema['views_test_data'] = array( + 'description' => 'Basic test table for Views tests.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'description' => "A person's name", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'age' => array( + 'description' => "The person's age", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0), + 'job' => array( + 'description' => "The person's job", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => 'Undefined', + ), + 'created' => array( + 'description' => "The creation date of this record", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'name' => array('name') + ), + 'indexes' => array( + 'ages' => array('age'), + ), + ); + return $schema; + } + + /** + * The views data definition. + */ + protected function viewsData() { + // Declaration of the base table. + $data['views_test_data']['table'] = array( + 'group' => t('Views test'), + 'base' => array( + 'field' => 'id', + 'title' => t('Views test data'), + 'help' => t('Users who have created accounts on your site.'), + ), + ); + + // Declaration of fields. + $data['views_test_data']['id'] = array( + 'title' => t('ID'), + 'help' => t('The test data ID'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['views_test_data']['name'] = array( + 'title' => t('Name'), + 'help' => t('The name of the person'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'string', + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['views_test_data']['age'] = array( + 'title' => t('Age'), + 'help' => t('The age of the person'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['views_test_data']['job'] = array( + 'title' => t('Job'), + 'help' => t('The job of the person'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'string', + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['views_test_data']['created'] = array( + 'title' => t('Created'), + 'help' => t('The creation date of this record'), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + 'sort' => array( + 'id' => 'date', + ), + ); + return $data; + } + + /** + * A very simple test dataset. + */ + protected function dataSet() { + return array( + array( + 'name' => 'John', + 'age' => 25, + 'job' => 'Singer', + 'created' => gmmktime(0, 0, 0, 1, 1, 2000), + ), + array( + 'name' => 'George', + 'age' => 27, + 'job' => 'Singer', + 'created' => gmmktime(0, 0, 0, 1, 2, 2000), + ), + array( + 'name' => 'Ringo', + 'age' => 28, + 'job' => 'Drummer', + 'created' => gmmktime(6, 30, 30, 1, 1, 2000), + ), + array( + 'name' => 'Paul', + 'age' => 26, + 'job' => 'Songwriter', + 'created' => gmmktime(6, 0, 0, 1, 1, 2000), + ), + array( + 'name' => 'Meredith', + 'age' => 30, + 'job' => 'Speaker', + 'created' => gmmktime(6, 30, 10, 1, 1, 2000), + ), + ); + } + + /** + * Build and return a basic view of the views_test_data table. + * + * @return Drupal\views\ViewExecutable + */ + protected function getBasicView() { + return $this->createViewFromConfig('test_view'); + } + + /** + * Creates a new View instance by creating directly from config data. + * + * @param string $view_name + * The name of the test view to create. + * + * @return Drupal\views\ViewExecutable + * A View instance. + */ + protected function createViewFromConfig($view_name) { + if (!module_exists('views_test_config')) { + module_enable(array('views_test_config')); + } + + $data = config("views.view.$view_name")->get(); + + $view = entity_create('view', $data); + $view = $view->getExecutable(); + $view->setDisplay(); + + return $view; + } + + /** + * Clones the view used in this test and sets the default display. + * + * @param Drupal\views\ViewStorage $original_view + * (optional) The view to clone. If not specified, the default view for the + * test will be used. + * + * @return Drupal\views\ViewExecutable + * A clone of the view. + */ + protected function getView($original_view = NULL) { + if (isset($original_view)) { + $view = $original_view->cloneView(); + } + else { + $view = $this->view->cloneView(); + } + $view->setDisplay(); + return $view; + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1ab4ae04ec0bbff87d6d4b5d8531c4e5c55492e --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php @@ -0,0 +1,141 @@ + 'Table Data', + 'description' => 'Tests the fetching of views data.', + 'group' => 'Views', + ); + } + + protected function setUp() { + parent::setUp(); + + $this->enableViewsTestModule(); + } + + /** + * Tests the views_fetch_data function. + * + * @see views_fetch_data + */ + public function testViewsFetchData() { + $table_name = 'views_test_data'; + $expected_data = $this->viewsData(); + + $data = views_fetch_data($table_name); + $this->assertEqual($data, $expected_data[$table_name], 'Make sure fetching views data by table works as expected.'); + + $data = views_fetch_data(); + $this->assertTrue(isset($data[$table_name]), 'Make sure the views_test_data info appears in the total views data.'); + $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.'); + + $data = views_fetch_data(NULL, TRUE); + $this->assertTrue(isset($data[$table_name]), 'Make sure the views_fetch_data appears in the total views data with reset = TRUE.'); + $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.'); + } + + /** + * Overrides Drupal\views\Tests\ViewTestBase::viewsData(). + */ + protected function viewsData() { + $data = parent::viewsData(); + + // Tweak the views data to have a base for testing views_fetch_fields(). + unset($data['views_test_data']['id']['field']); + unset($data['views_test_data']['name']['argument']); + unset($data['views_test_data']['age']['filter']); + unset($data['views_test_data']['job']['sort']); + $data['views_test_data']['created']['area']['id'] = 'text'; + $data['views_test_data']['age']['area']['id'] = 'text'; + $data['views_test_data']['age']['area']['sub_type'] = 'header'; + $data['views_test_data']['job']['area']['id'] = 'text'; + $data['views_test_data']['job']['area']['sub_type'] = array('header', 'footer'); + + + return $data; + } + + + /** + * Tests the views_fetch_fields function(). + */ + public function testViewsFetchFields() { + module_load_include('inc', 'views_ui', 'admin'); + + $expected = array( + 'field' => array( + 'name', + 'age', + 'job', + 'created', + ), + 'argument' => array( + 'id', + 'age', + 'job', + 'created', + ), + 'filter' => array( + 'id', + 'name', + 'job', + 'created', + ), + 'sort' => array( + 'id', + 'name', + 'age', + 'created', + ), + 'area' => array( + 'created', + 'job', + 'age' + ), + 'header' => array( + 'created', + 'job', + 'age' + ), + 'footer' => array( + 'created', + 'job', + ), + ); + + $handler_types = array('field', 'argument', 'filter', 'sort', 'area'); + foreach ($handler_types as $handler_type) { + $fields = views_fetch_fields('views_test_data', $handler_type); + $expected_keys = array_walk($expected[$handler_type], function(&$item) { + $item = "views_test_data.$item"; + }); + $this->assertEqual($expected_keys, array_keys($fields), format_string('Handlers of type @handler_type are listed as expected.', array('@handler_type' => $handler_type))); + } + + // Check for subtype filtering, so header and footer. + foreach (array('header', 'footer') as $sub_type) { + $fields = views_fetch_fields('views_test_data', 'area', FALSE, $sub_type); + + $expected_keys = array_walk($expected[$sub_type], function(&$item) { + $item = "views_test_data.$item"; + }); + $this->assertEqual($expected_keys, array_keys($fields), format_string('Sub_type @sub_type is filtered as expected.', array('@sub_type' => $sub_type))); + } + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php new file mode 100644 index 0000000000000000000000000000000000000000..dbf48ff0a558ce42ea73cb6d9365e9aa04a58011 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php @@ -0,0 +1,140 @@ + 'Basic functionality', + 'description' => 'Test creating basic views with the wizard and viewing them on the listing page.', + 'group' => 'Views Wizard', + ); + } + + function testViewsWizardAndListing() { + $this->drupalCreateContentType(array('type' => 'article')); + $this->drupalCreateContentType(array('type' => 'page')); + + // Check if we can access the main views admin page. + $this->drupalGet('admin/structure/views'); + $this->assertText(t('Add new view')); + + // Create a simple and not at all useful view. + $view1 = array(); + $view1['human_name'] = $this->randomName(16); + $view1['name'] = strtolower($this->randomName(16)); + $view1['description'] = $this->randomName(16); + $view1['page[create]'] = FALSE; + $this->drupalPost('admin/structure/views/add', $view1, t('Save & exit')); + $this->assertResponse(200); + $this->assertText(t('Your view was saved. You may edit it from the list below.')); + $this->assertText($view1['human_name']); + $this->assertText($view1['description']); + // @todo For now, clone is being left to config.module to solve. + foreach (array('delete', 'edit') as $operation) { + $this->assertLinkByHref(url('admin/structure/views/view/' . $view1['name'] . '/' . $operation)); + } + + // This view should not have a block. + $this->drupalGet('admin/structure/block'); + $this->assertNoText('View: ' . $view1['human_name']); + + // Create two nodes. + $node1 = $this->drupalCreateNode(array('type' => 'page')); + $node2 = $this->drupalCreateNode(array('type' => 'article')); + + // Now create a page with simple node listing and an attached feed. + $view2 = array(); + $view2['human_name'] = $this->randomName(16); + $view2['name'] = strtolower($this->randomName(16)); + $view2['description'] = $this->randomName(16); + $view2['page[create]'] = 1; + $view2['page[title]'] = $this->randomName(16); + $view2['page[path]'] = $this->randomName(16); + $view2['page[feed]'] = 1; + $view2['page[feed_properties][path]'] = $this->randomName(16); + $this->drupalPost('admin/structure/views/add', $view2, t('Save & exit')); + $this->assertResponse(200); + + // Since the view has a page, we expect to be automatically redirected to + // it. + $this->assertUrl($view2['page[path]']); + $this->assertText($view2['page[title]']); + $this->assertText($node1->label()); + $this->assertText($node2->label()); + + // Check if we have the feed. + $this->assertLinkByHref(url($view2['page[feed_properties][path]'])); + $this->drupalGet($view2['page[feed_properties][path]']); + $this->assertRaw('assertText($view2['page[title]']); + $this->assertRaw(url('node/' . $node1->nid, array('absolute' => TRUE))); + $this->assertText($node1->label()); + $this->assertRaw(url('node/' . $node2->nid, array('absolute' => TRUE))); + $this->assertText($node2->label()); + + // Go back to the views page and check if this view is there. + $this->drupalGet('admin/structure/views'); + $this->assertText($view2['human_name']); + $this->assertText($view2['description']); + $this->assertLinkByHref(url($view2['page[path]'])); + + // This view should not have a block. + $this->drupalGet('admin/structure/block'); + $this->assertNoText('View: ' . $view2['human_name']); + + // Create a view with a page and a block, and filter the listing. + $view3 = array(); + $view3['human_name'] = $this->randomName(16); + $view3['name'] = strtolower($this->randomName(16)); + $view3['description'] = $this->randomName(16); + $view3['show[wizard_key]'] = 'node'; + $view3['show[type]'] = 'page'; + $view3['page[create]'] = 1; + $view3['page[title]'] = $this->randomName(16); + $view3['page[path]'] = $this->randomName(16); + $view3['block[create]'] = 1; + $view3['block[title]'] = $this->randomName(16); + $this->drupalPost('admin/structure/views/add', $view3, t('Save & exit')); + $this->assertResponse(200); + + // Make sure the view only displays the node we expect. + $this->assertUrl($view3['page[path]']); + $this->assertText($view3['page[title]']); + $this->assertText($node1->label()); + $this->assertNoText($node2->label()); + + // Go back to the views page and check if this view is there. + $this->drupalGet('admin/structure/views'); + $this->assertText($view3['human_name']); + $this->assertText($view3['description']); + $this->assertLinkByHref(url($view3['page[path]'])); + + // Put the block into the first sidebar region. + $this->drupalGet('admin/structure/block'); + $this->assertText('View: ' . $view3['human_name']); + $edit = array(); + $edit["blocks[views_{$view3['name']}-block_1][region]"] = 'sidebar_first'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + + // Visit a random page (not the one that displays the view itself) and look + // for the expected node title in the block. + $this->drupalGet('user'); + $this->assertText($node1->label()); + $this->assertNoText($node2->label()); + + // Make sure the listing page doesn't show disabled default views. + $this->assertNoText('tracker', t('Default tracker view does not show on the listing page.')); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3e69105fa315763a99fd4df3632f46a2dec7ac8d --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php @@ -0,0 +1,98 @@ + 'Items per page functionality', + 'description' => 'Test the ability of the views wizard to specify the number of items per page.', + 'group' => 'Views Wizard', + ); + } + + /** + * Tests the number of items per page. + */ + function testItemsPerPage() { + $this->drupalCreateContentType(array('type' => 'article')); + + // Create articles, each with a different creation time so that we can do a + // meaningful sort. + $node1 = $this->drupalCreateNode(array('type' => 'article', 'created' => REQUEST_TIME)); + $node2 = $this->drupalCreateNode(array('type' => 'article', 'created' => REQUEST_TIME + 1)); + $node3 = $this->drupalCreateNode(array('type' => 'article', 'created' => REQUEST_TIME + 2)); + $node4 = $this->drupalCreateNode(array('type' => 'article', 'created' => REQUEST_TIME + 3)); + $node5 = $this->drupalCreateNode(array('type' => 'article', 'created' => REQUEST_TIME + 4)); + + // Create a page. This should never appear in the view created below. + $page_node = $this->drupalCreateNode(array('type' => 'page', 'created' => REQUEST_TIME + 2)); + + // Create a view that sorts newest first, and shows 4 items in the page and + // 3 in the block. + $view = array(); + $view['human_name'] = $this->randomName(16); + $view['name'] = strtolower($this->randomName(16)); + $view['description'] = $this->randomName(16); + $view['show[wizard_key]'] = 'node'; + $view['show[type]'] = 'article'; + $view['show[sort]'] = 'created:DESC'; + $view['page[create]'] = 1; + $view['page[title]'] = $this->randomName(16); + $view['page[path]'] = $this->randomName(16); + $view['page[items_per_page]'] = 4; + $view['block[create]'] = 1; + $view['block[title]'] = $this->randomName(16); + $view['block[items_per_page]'] = 3; + $this->drupalPost('admin/structure/views/add', $view, t('Save & exit')); + $this->assertResponse(200); + + // Make sure the page display shows the nodes we expect, and that they + // appear in the expected order. + $this->assertUrl($view['page[path]']); + $this->assertText($view['page[title]']); + $content = $this->drupalGetContent(); + $this->assertText($node5->label()); + $this->assertText($node4->label()); + $this->assertText($node3->label()); + $this->assertText($node2->label()); + $this->assertNoText($node1->label()); + $this->assertNoText($page_node->label()); + $pos5 = strpos($content, $node5->label()); + $pos4 = strpos($content, $node4->label()); + $pos3 = strpos($content, $node3->label()); + $pos2 = strpos($content, $node2->label()); + $this->assertTrue($pos5 < $pos4 && $pos4 < $pos3 && $pos3 < $pos2, t('The nodes appear in the expected order in the page display.')); + + // Put the block into the first sidebar region, visit a page that displays + // the block, and check that the nodes we expect appear in the correct + // order. + $this->drupalGet('admin/structure/block'); + $this->assertText('View: ' . $view['human_name']); + $edit = array(); + $edit["blocks[views_{$view['name']}-block_1][region]"] = 'sidebar_first'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + $this->drupalGet('user'); + $content = $this->drupalGetContent(); + $this->assertText($node5->label()); + $this->assertText($node4->label()); + $this->assertText($node3->label()); + $this->assertNoText($node2->label()); + $this->assertNoText($node1->label()); + $this->assertNoText($page_node->label()); + $pos5 = strpos($content, $node5->label()); + $pos4 = strpos($content, $node4->label()); + $pos3 = strpos($content, $node3->label()); + $this->assertTrue($pos5 < $pos4 && $pos4 < $pos3, t('The nodes appear in the expected order in the block display.')); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/MenuTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/MenuTest.php new file mode 100644 index 0000000000000000000000000000000000000000..13405347f62df11799691c0756e82cc833df98e7 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/MenuTest.php @@ -0,0 +1,60 @@ + 'Menu functionality', + 'description' => 'Test the ability of the views wizard to put views in a menu.', + 'group' => 'Views Wizard', + ); + } + + /** + * Tests the menu functionality. + */ + function testMenus() { + // Create a view with a page display and a menu link in the Main Menu. + $view = array(); + $view['human_name'] = $this->randomName(16); + $view['name'] = strtolower($this->randomName(16)); + $view['description'] = $this->randomName(16); + $view['page[create]'] = 1; + $view['page[title]'] = $this->randomName(16); + $view['page[path]'] = $this->randomName(16); + $view['page[link]'] = 1; + $view['page[link_properties][menu_name]'] = 'main-menu'; + $view['page[link_properties][title]'] = $this->randomName(16); + $this->drupalPost('admin/structure/views/add', $view, t('Save & exit')); + $this->assertResponse(200); + + // Make sure there is a link to the view from the front page (where we + // expect the main menu to display). + $this->drupalGet(''); + $this->assertResponse(200); + $this->assertLink($view['page[link_properties][title]']); + $this->assertLinkByHref(url($view['page[path]'])); + + // Make sure the link is associated with the main menu. + $links = menu_load_links('main-menu'); + $found = FALSE; + foreach ($links as $link) { + if ($link['link_path'] == $view['page[path]']) { + $found = TRUE; + break; + } + } + $this->assertTrue($found, t('Found a link to %path in the main menu', array('%path' => $view['page[path]']))); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/SortingTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/SortingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..888d60cc9eb4e2bc56dd418bbb0df00d251ff85e --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/SortingTest.php @@ -0,0 +1,82 @@ + 'Sorting functionality', + 'description' => 'Test the ability of the views wizard to create views with sorts.', + 'group' => 'Views Wizard', + ); + } + + /** + * Tests the sorting functionality. + */ + function testSorting() { + // Create nodes, each with a different creation time so that we can do a + // meaningful sort. + $node1 = $this->drupalCreateNode(array('created' => REQUEST_TIME)); + $node2 = $this->drupalCreateNode(array('created' => REQUEST_TIME + 1)); + $node3 = $this->drupalCreateNode(array('created' => REQUEST_TIME + 2)); + + // Create a view that sorts oldest first. + $view1 = array(); + $view1['human_name'] = $this->randomName(16); + $view1['name'] = strtolower($this->randomName(16)); + $view1['description'] = $this->randomName(16); + $view1['show[sort]'] = 'created:ASC'; + $view1['page[create]'] = 1; + $view1['page[title]'] = $this->randomName(16); + $view1['page[path]'] = $this->randomName(16); + $this->drupalPost('admin/structure/views/add', $view1, t('Save & exit')); + $this->assertResponse(200); + + // Make sure the view shows the nodes in the expected order. + $this->assertUrl($view1['page[path]']); + $this->assertText($view1['page[title]']); + $content = $this->drupalGetContent(); + $this->assertText($node1->label()); + $this->assertText($node2->label()); + $this->assertText($node3->label()); + $pos1 = strpos($content, $node1->label()); + $pos2 = strpos($content, $node2->label()); + $pos3 = strpos($content, $node3->label()); + $this->assertTrue($pos1 < $pos2 && $pos2 < $pos3, t('The nodes appear in the expected order in a view that sorts by oldest first.')); + + // Create a view that sorts newest first. + $view2 = array(); + $view2['human_name'] = $this->randomName(16); + $view2['name'] = strtolower($this->randomName(16)); + $view2['description'] = $this->randomName(16); + $view2['show[sort]'] = 'created:DESC'; + $view2['page[create]'] = 1; + $view2['page[title]'] = $this->randomName(16); + $view2['page[path]'] = $this->randomName(16); + $this->drupalPost('admin/structure/views/add', $view2, t('Save & exit')); + $this->assertResponse(200); + + // Make sure the view shows the nodes in the expected order. + $this->assertUrl($view2['page[path]']); + $this->assertText($view2['page[title]']); + $content = $this->drupalGetContent(); + $this->assertText($node3->label()); + $this->assertText($node2->label()); + $this->assertText($node1->label()); + $pos3 = strpos($content, $node3->label()); + $pos2 = strpos($content, $node2->label()); + $pos1 = strpos($content, $node1->label()); + $this->assertTrue($pos3 < $pos2 && $pos2 < $pos1, t('The nodes appear in the expected order in a view that sorts by newest first.')); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cf3d19e90a4b543c539371eae02c49879b799c4d --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php @@ -0,0 +1,193 @@ + 'Taxonomy functionality', + 'description' => 'Test the ability of the views wizard to create views filtered by taxonomy.', + 'group' => 'Views Wizard', + ); + } + + function setUp() { + parent::setUp(); + + // Create two content types. One will have an autocomplete tagging field, + // and one won't. + $this->node_type_with_tags = $this->drupalCreateContentType(); + $this->node_type_without_tags = $this->drupalCreateContentType(); + + // Create the vocabulary for the tag field. + $this->tag_vocabulary = entity_create('taxonomy_vocabulary', array( + 'name' => 'Views testing tags', + 'machine_name' => 'views_testing_tags', + )); + $this->tag_vocabulary->save(); + + // Create the tag field itself. + $this->tag_field = array( + 'field_name' => 'field_views_testing_tags', + 'type' => 'taxonomy_term_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $this->tag_vocabulary->machine_name, + 'parent' => 0, + ), + ), + ), + ); + field_create_field($this->tag_field); + + // Create an instance of the tag field on one of the content types, and + // configure it to display an autocomplete widget. + $this->tag_instance = array( + 'field_name' => 'field_views_testing_tags', + 'entity_type' => 'node', + 'bundle' => $this->node_type_with_tags->type, + 'widget' => array( + 'type' => 'taxonomy_autocomplete', + ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), + ); + field_create_instance($this->tag_instance); + } + + /** + * Tests the "tagged with" functionality. + */ + function testTaggedWith() { + // In this test we will only create nodes that have an instance of the tag + // field. + $node_add_path = 'node/add/' . $this->node_type_with_tags->type; + + // Create three nodes, with different tags. + $tag_field = $this->tag_field['field_name'] . '[' . LANGUAGE_NOT_SPECIFIED . ']'; + $edit = array(); + $edit['title'] = $node_tag1_title = $this->randomName(); + $edit[$tag_field] = 'tag1'; + $this->drupalPost($node_add_path, $edit, t('Save')); + $edit = array(); + $edit['title'] = $node_tag1_tag2_title = $this->randomName(); + $edit[$tag_field] = 'tag1, tag2'; + $this->drupalPost($node_add_path, $edit, t('Save')); + $edit = array(); + $edit['title'] = $node_no_tags_title = $this->randomName(); + $this->drupalPost($node_add_path, $edit, t('Save')); + + // Create a view that filters by taxonomy term "tag1". It should show only + // the two nodes from above that are tagged with "tag1". + $view1 = array(); + // First select the node type and update the form so the correct tag field + // is used. + $view1['show[type]'] = $this->node_type_with_tags->type; + $this->drupalPost('admin/structure/views/add', $view1, t('Update "of type" choice')); + // Now resubmit the entire form to the same URL. + $view1['human_name'] = $this->randomName(16); + $view1['name'] = strtolower($this->randomName(16)); + $view1['description'] = $this->randomName(16); + $view1['show[tagged_with]'] = 'tag1'; + $view1['page[create]'] = 1; + $view1['page[title]'] = $this->randomName(16); + $view1['page[path]'] = $this->randomName(16); + $this->drupalPost(NULL, $view1, t('Save & exit')); + $this->assertResponse(200); + // Visit the page and check that the nodes we expect are present and the + // ones we don't expect are absent. + $this->drupalGet($view1['page[path]']); + $this->assertText($node_tag1_title); + $this->assertText($node_tag1_tag2_title); + $this->assertNoText($node_no_tags_title); + + // Create a view that filters by taxonomy term "tag2". It should show only + // the one node from above that is tagged with "tag2". + $view2 = array(); + $view2['show[type]'] = $this->node_type_with_tags->type; + $this->drupalPost('admin/structure/views/add', $view2, t('Update "of type" choice')); + $this->assertResponse(200); + $view2['human_name'] = $this->randomName(16); + $view2['name'] = strtolower($this->randomName(16)); + $view2['description'] = $this->randomName(16); + $view2['show[tagged_with]'] = 'tag2'; + $view2['page[create]'] = 1; + $view2['page[title]'] = $this->randomName(16); + $view2['page[path]'] = $this->randomName(16); + $this->drupalPost(NULL, $view2, t('Save & exit')); + $this->assertResponse(200); + $this->drupalGet($view2['page[path]']); + $this->assertNoText($node_tag1_title); + $this->assertText($node_tag1_tag2_title); + $this->assertNoText($node_no_tags_title); + } + + /** + * Tests that the "tagged with" form element only shows for node types that support it. + */ + function testTaggedWithByNodeType() { + // The tagging field is associated with one of our node types only. So the + // "tagged with" form element on the view wizard should appear on the form + // by default (when the wizard is configured to display all content) and + // also when the node type that has the tagging field is selected, but not + // when the node type that doesn't have the tagging field is selected. + $tags_xpath = '//input[@name="show[tagged_with]"]'; + $this->drupalGet('admin/structure/views/add'); + $this->assertFieldByXpath($tags_xpath); + $view['show[type]'] = $this->node_type_with_tags->type; + $this->drupalPost('admin/structure/views/add', $view, t('Update "of type" choice')); + $this->assertFieldByXpath($tags_xpath); + $view['show[type]'] = $this->node_type_without_tags->type; + $this->drupalPost(NULL, $view, t('Update "of type" choice')); + $this->assertNoFieldByXpath($tags_xpath); + + // If we add an instance of the tagging field to the second node type, the + // "tagged with" form element should not appear for it too. + $instance = $this->tag_instance; + $instance['bundle'] = $this->node_type_without_tags->type; + field_create_instance($instance); + $view['show[type]'] = $this->node_type_with_tags->type; + $this->drupalPost('admin/structure/views/add', $view, t('Update "of type" choice')); + $this->assertFieldByXpath($tags_xpath); + $view['show[type]'] = $this->node_type_without_tags->type; + $this->drupalPost(NULL, $view, t('Update "of type" choice')); + $this->assertFieldByXpath($tags_xpath); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php new file mode 100644 index 0000000000000000000000000000000000000000..acf02eb0d310c0587e14beb19603fc38e33985d6 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php @@ -0,0 +1,32 @@ +drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view revisions')); + $this->drupalLogin($views_admin); + } + +} diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php new file mode 100644 index 0000000000000000000000000000000000000000..36aaee80ab9014aed4528e82b23df93ddce4019f --- /dev/null +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -0,0 +1,2233 @@ +style_plugin + * + * @var string + */ + public $plugin_name; + + /** + * The options used by the style plugin of this running view. + * + * @todo To be able to remove it, Drupal\views\Plugin\views\argument\ArgumentPluginBase::default_summary() + * should instantiate the style plugin. + * @var array + */ + public $style_options; + + /** + * The rendered output of the exposed form. + * + * @var string + */ + public $exposed_widgets; + + /** + * If this view has been previewed. + * + * @var bool + */ + public $preview; + + /** + * Force the query to calculate the total number of results. + * + * @todo Move to the query. + * + * @var bool + */ + public $get_total_rows; + + /** + * Indicates if the sorts have been built. + * + * @todo Group with other static properties. + * + * @var bool + */ + public $build_sort; + + /** + * Stores the many-to-one tables for performance. + * + * @var array + */ + public $many_to_one_tables; + + /** + * A unique identifier which allows to update multiple views output via js. + * + * @var string + */ + public $dom_id; + + /** + * Constructs a new ViewExecutable object. + * + * @param Drupal\views\ViewStorage $storage + * The view config entity the actual information is stored on. + */ + public function __construct(ViewStorage $storage) { + // Reference the storage and the executable to each other. + $this->storage = $storage; + $this->storage->setExecutable($this); + } + + /** + * @todo. + */ + public function save() { + $this->storage->save(); + } + + /** + * Returns a list of the sub-object types used by this view. These types are + * stored on the display, and are used in the build process. + */ + public function displayObjects() { + return array('argument', 'field', 'sort', 'filter', 'relationship', 'header', 'footer', 'empty'); + } + + /** + * Set the arguments that come to this view. Usually from the URL + * but possibly from elsewhere. + */ + public function setArguments($args) { + $this->args = $args; + } + + /** + * Change/Set the current page for the pager. + */ + public function setCurrentPage($page) { + $this->current_page = $page; + + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + return $this->pager->set_current_page($page); + } + } + + /** + * Get the current page from the pager. + */ + public function getCurrentPage() { + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + return $this->pager->get_current_page(); + } + + if (isset($this->current_page)) { + return $this->current_page; + } + } + + /** + * Get the items per page from the pager. + */ + public function getItemsPerPage() { + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + return $this->pager->get_items_per_page(); + } + + if (isset($this->items_per_page)) { + return $this->items_per_page; + } + } + + /** + * Set the items per page on the pager. + */ + public function setItemsPerPage($items_per_page) { + $this->items_per_page = $items_per_page; + + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + $this->pager->set_items_per_page($items_per_page); + } + } + + /** + * Get the pager offset from the pager. + */ + public function getOffset() { + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + return $this->pager->get_offset(); + } + + if (isset($this->offset)) { + return $this->offset; + } + } + + /** + * Set the offset on the pager. + */ + public function setOffset($offset) { + $this->offset = $offset; + + // If the pager is already initialized, pass it through to the pager. + if (!empty($this->pager)) { + $this->pager->set_offset($offset); + } + } + + /** + * Determine if the pager actually uses a pager. + */ + public function usePager() { + if (!empty($this->pager)) { + return $this->pager->use_pager(); + } + } + + /** + * Whether or not AJAX should be used. If AJAX is used, paging, + * tablesorting and exposed filters will be fetched via an AJAX call + * rather than a page refresh. + */ + public function setUseAJAX($use_ajax) { + $this->use_ajax = $use_ajax; + } + + /** + * Set the exposed filters input to an array. If unset they will be taken + * from $_GET when the time comes. + */ + public function setExposedInput($filters) { + $this->exposed_input = $filters; + } + + /** + * Figure out what the exposed input for this view is. + */ + public function getExposedInput() { + // Fill our input either from $_GET or from something previously set on the + // view. + if (empty($this->exposed_input)) { + $this->exposed_input = drupal_container()->get('request')->query->all(); + // unset items that are definitely not our input: + foreach (array('page', 'q') as $key) { + if (isset($this->exposed_input[$key])) { + unset($this->exposed_input[$key]); + } + } + + // If we have no input at all, check for remembered input via session. + + // If filters are not overridden, store the 'remember' settings on the + // default display. If they are, store them on this display. This way, + // multiple displays in the same view can share the same filters and + // remember settings. + $display_id = ($this->display_handler->isDefaulted('filters')) ? 'default' : $this->current_display; + + if (empty($this->exposed_input) && !empty($_SESSION['views'][$this->storage->name][$display_id])) { + $this->exposed_input = $_SESSION['views'][$this->storage->name][$display_id]; + } + } + + return $this->exposed_input; + } + + /** + * Set the display for this view and initialize the display handler. + */ + public function initDisplay() { + if (isset($this->current_display)) { + return TRUE; + } + + // Instantiate all displays + foreach (array_keys($this->storage->display) as $id) { + $this->displayHandlers[$id] = views_get_plugin('display', $this->storage->display[$id]['display_plugin']); + if (!empty($this->displayHandlers[$id])) { + // Initialize the new display handler with data. + $this->displayHandlers[$id]->init($this, $this->storage->display[$id]); + // If this is NOT the default display handler, let it know which is + // since it may well utilize some data from the default. + // This assumes that the 'default' handler is always first. It always + // is. Make sure of it. + if ($id != 'default') { + $this->displayHandlers[$id]->default_display =& $this->displayHandlers['default']; + } + } + } + + $this->current_display = 'default'; + $this->display_handler = $this->displayHandlers['default']; + + return TRUE; + } + + /** + * Get the first display that is accessible to the user. + * + * @param array|string $displays + * Either a single display id or an array of display ids. + * + * @return string + * The first accessible display id, at least default. + */ + public function chooseDisplay($displays) { + if (!is_array($displays)) { + return $displays; + } + + $this->initDisplay(); + + foreach ($displays as $display_id) { + if ($this->displayHandlers[$display_id]->access()) { + return $display_id; + } + } + + return 'default'; + } + + /** + * Set the display as current. + * + * @param $display_id + * The id of the display to mark as current. + */ + public function setDisplay($display_id = NULL) { + // If we have not already initialized the display, do so. But be careful. + if (empty($this->current_display)) { + $this->initDisplay(); + + // If handlers were not initialized, and no argument was sent, set up + // to the default display. + if (empty($display_id)) { + $display_id = 'default'; + } + } + + $display_id = $this->chooseDisplay($display_id); + + // If no display id sent in and one wasn't chosen above, we're finished. + if (empty($display_id)) { + return FALSE; + } + + // Ensure the requested display exists. + if (empty($this->displayHandlers[$display_id])) { + $display_id = 'default'; + if (empty($this->displayHandlers[$display_id])) { + debug('set_display() called with invalid display ID @display.', array('@display' => $display_id)); + return FALSE; + } + } + + // Set the current display. + $this->current_display = $display_id; + + // Ensure requested display has a working handler. + if (empty($this->displayHandlers[$display_id])) { + return FALSE; + } + + // Set a shortcut + $this->display_handler = $this->displayHandlers[$display_id]; + + return TRUE; + } + + /** + * Find and initialize the style plugin. + * + * Note that arguments may have changed which style plugin we use, so + * check the view object first, then ask the display handler. + */ + public function initStyle() { + if (isset($this->style_plugin)) { + return is_object($this->style_plugin); + } + + if (!isset($this->plugin_name)) { + $style = $this->display_handler->getOption('style'); + $this->plugin_name = $style['type']; + $this->style_options = $style['options']; + } + + $this->style_plugin = views_get_plugin('style', $this->plugin_name); + + if (empty($this->style_plugin)) { + return FALSE; + } + + // init the new style handler with data. + $this->style_plugin->init($this, $this->display_handler, $this->style_options); + return TRUE; + } + + /** + * Acquire and attach all of the handlers. + */ + public function initHandlers() { + $this->initDisplay(); + if (empty($this->inited)) { + foreach ($this::viewsHandlerTypes() as $key => $info) { + $this->_initHandler($key, $info); + } + $this->inited = TRUE; + } + } + + /** + * Initialize the pager + * + * Like style initialization, pager initialization is held until late + * to allow for overrides. + */ + public function initPager() { + if (!isset($this->pager)) { + $this->pager = $this->display_handler->getPlugin('pager'); + + if ($this->pager->use_pager()) { + $this->pager->set_current_page($this->current_page); + } + + // These overrides may have been set earlier via $view->set_* + // functions. + if (isset($this->items_per_page)) { + $this->pager->set_items_per_page($this->items_per_page); + } + + if (isset($this->offset)) { + $this->pager->set_offset($this->offset); + } + } + } + + /** + * Render the pager, if necessary. + */ + public function renderPager($exposed_input) { + if (!empty($this->pager) && $this->pager->use_pager()) { + return $this->pager->render($exposed_input); + } + + return ''; + } + + /** + * Create a list of base tables eligible for this view. Used primarily + * for the UI. Display must be already initialized. + */ + public function getBaseTables() { + $base_tables = array( + $this->storage->base_table => TRUE, + '#global' => TRUE, + ); + + foreach ($this->display_handler->getHandlers('relationship') as $handler) { + $base_tables[$handler->definition['base']] = TRUE; + } + return $base_tables; + } + + /** + * Run the preQuery() on all active handlers. + */ + protected function _preQuery() { + foreach ($this::viewsHandlerTypes() as $key => $info) { + $handlers = &$this->$key; + $position = 0; + foreach ($handlers as $id => $handler) { + $handlers[$id]->position = $position; + $handlers[$id]->preQuery(); + $position++; + } + } + } + + /** + * Run the postExecute() on all active handlers. + */ + protected function _postExecute() { + foreach ($this::viewsHandlerTypes() as $key => $info) { + $handlers = &$this->$key; + foreach ($handlers as $id => $handler) { + $handlers[$id]->postExecute($this->result); + } + } + } + + /** + * Attach all of the handlers for each type. + * + * @param $key + * One of 'argument', 'field', 'sort', 'filter', 'relationship' + * @param $info + * The $info from viewsHandlerTypes for this object. + */ + protected function _initHandler($key, $info) { + // Load the requested items from the display onto the object. + $this->$key = $this->display_handler->getHandlers($key); + + // This reference deals with difficult PHP indirection. + $handlers = &$this->$key; + + // Run through and test for accessibility. + foreach ($handlers as $id => $handler) { + if (!$handler->access()) { + unset($handlers[$id]); + } + } + } + + /** + * Build all the arguments. + */ + protected function _buildArguments() { + // Initially, we want to build sorts and fields. This can change, though, + // if we get a summary view. + if (empty($this->argument)) { + return TRUE; + } + + // build arguments. + $position = -1; + + // Create a title for use in the breadcrumb trail. + $title = $this->display_handler->getOption('title'); + + $this->build_info['breadcrumb'] = array(); + $breadcrumb_args = array(); + $substitutions = array(); + + $status = TRUE; + + // Iterate through each argument and process. + foreach ($this->argument as $id => $arg) { + $position++; + $argument = &$this->argument[$id]; + + if ($argument->broken()) { + continue; + } + + $argument->setRelationship(); + + $arg = isset($this->args[$position]) ? $this->args[$position] : NULL; + $argument->position = $position; + + if (isset($arg) || $argument->has_default_argument()) { + if (!isset($arg)) { + $arg = $argument->get_default_argument(); + // make sure default args get put back. + if (isset($arg)) { + $this->args[$position] = $arg; + } + // remember that this argument was computed, not passed on the URL. + $argument->is_default = TRUE; + } + + // Set the argument, which will also validate that the argument can be set. + if (!$argument->set_argument($arg)) { + $status = $argument->validateFail($arg); + break; + } + + if ($argument->is_exception()) { + $arg_title = $argument->exception_title(); + } + else { + $arg_title = $argument->get_title(); + $argument->query($this->display_handler->useGroupBy()); + } + + // Add this argument's substitution + $substitutions['%' . ($position + 1)] = $arg_title; + $substitutions['!' . ($position + 1)] = strip_tags(decode_entities($arg)); + + // Since we're really generating the breadcrumb for the item above us, + // check the default action of this argument. + if ($this->display_handler->usesBreadcrumb() && $argument->uses_breadcrumb()) { + $path = $this->getUrl($breadcrumb_args); + if (strpos($path, '%') === FALSE) { + if (!empty($argument->options['breadcrumb_enable']) && !empty($argument->options['breadcrumb'])) { + $breadcrumb = $argument->options['breadcrumb']; + } + else { + $breadcrumb = $title; + } + $this->build_info['breadcrumb'][$path] = str_replace(array_keys($substitutions), $substitutions, $breadcrumb); + } + } + + // Allow the argument to muck with this breadcrumb. + $argument->set_breadcrumb($this->build_info['breadcrumb']); + + // Test to see if we should use this argument's title + if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) { + $title = $argument->options['title']; + } + + $breadcrumb_args[] = $arg; + } + else { + // determine default condition and handle. + $status = $argument->default_action(); + break; + } + + // Be safe with references and loops: + unset($argument); + } + + // set the title in the build info. + if (!empty($title)) { + $this->build_info['title'] = $title; + } + + // Store the arguments for later use. + $this->build_info['substitutions'] = $substitutions; + + return $status; + } + + /** + * Do some common building initialization. + */ + public function initQuery() { + if (!empty($this->query)) { + $class = get_class($this->query); + if ($class && $class != 'stdClass') { + // return if query is already initialized. + return TRUE; + } + } + + // Create and initialize the query object. + $views_data = views_fetch_data($this->storage->base_table); + $this->storage->base_field = !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : ''; + if (!empty($views_data['table']['base']['database'])) { + $this->base_database = $views_data['table']['base']['database']; + } + + // Load the options. + $query_options = $this->display_handler->getOption('query'); + + // Create and initialize the query object. + $plugin = !empty($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query'; + $this->query = views_get_plugin('query', $plugin); + + if (empty($this->query)) { + return FALSE; + } + + $this->query->init($this->storage->base_table, $this->storage->base_field, $query_options['options']); + return TRUE; + } + + /** + * Build the query for the view. + */ + public function build($display_id = NULL) { + if (!empty($this->built)) { + return; + } + + if (empty($this->current_display) || $display_id) { + if (!$this->setDisplay($display_id)) { + return FALSE; + } + } + + // Let modules modify the view just prior to building it. + foreach (module_implements('views_pre_build') as $module) { + $function = $module . '_views_pre_build'; + $function($this); + } + + // Attempt to load from cache. + // @todo Load a build_info from cache. + + $start = microtime(TRUE); + // If that fails, let's build! + $this->build_info = array( + 'query' => '', + 'count_query' => '', + 'query_args' => array(), + ); + + $this->initQuery(); + + // Call a module hook and see if it wants to present us with a + // pre-built query or instruct us not to build the query for + // some reason. + // @todo: Implement this. Use the same mechanism Panels uses. + + // Run through our handlers and ensure they have necessary information. + $this->initHandlers(); + + // Let the handlers interact with each other if they really want. + $this->_preQuery(); + + if ($this->display_handler->usesExposed()) { + $exposed_form = $this->display_handler->getPlugin('exposed_form'); + $this->exposed_widgets = $exposed_form->render_exposed_form(); + if (form_set_error() || !empty($this->build_info['abort'])) { + $this->built = TRUE; + // Don't execute the query, but rendering will still be executed to display the empty text. + $this->executed = TRUE; + return empty($this->build_info['fail']); + } + } + + // Build all the relationships first thing. + $this->_build('relationship'); + + // Set the filtering groups. + if (!empty($this->filter)) { + $filter_groups = $this->display_handler->getOption('filter_groups'); + if ($filter_groups) { + $this->query->set_group_operator($filter_groups['operator']); + foreach ($filter_groups['groups'] as $id => $operator) { + $this->query->set_where_group($operator, $id); + } + } + } + + // Build all the filters. + $this->_build('filter'); + + $this->build_sort = TRUE; + + // Arguments can, in fact, cause this whole thing to abort. + if (!$this->_buildArguments()) { + $this->build_time = microtime(TRUE) - $start; + $this->attachDisplays(); + return $this->built; + } + + // Initialize the style; arguments may have changed which style we use, + // so waiting as long as possible is important. But we need to know + // about the style when we go to build fields. + if (!$this->initStyle()) { + $this->build_info['fail'] = TRUE; + return FALSE; + } + + if ($this->style_plugin->usesFields()) { + $this->_build('field'); + } + + // Build our sort criteria if we were instructed to do so. + if (!empty($this->build_sort)) { + // Allow the style handler to deal with sorting. + if ($this->style_plugin->build_sort()) { + $this->_build('sort'); + } + // allow the plugin to build second sorts as well. + $this->style_plugin->build_sort_post(); + } + + // Allow area handlers to affect the query. + $this->_build('header'); + $this->_build('footer'); + $this->_build('empty'); + + // Allow display handler to affect the query: + $this->display_handler->query($this->display_handler->useGroupBy()); + + // Allow style handler to affect the query: + $this->style_plugin->query($this->display_handler->useGroupBy()); + + // Allow exposed form to affect the query: + if (isset($exposed_form)) { + $exposed_form->query(); + } + + if (config('views.settings')->get('sql_signature')) { + $this->query->add_signature($this); + } + + // Let modules modify the query just prior to finalizing it. + $this->query->alter($this); + + // Only build the query if we weren't interrupted. + if (empty($this->built)) { + // Build the necessary info to execute the query. + $this->query->build($this); + } + + $this->built = TRUE; + $this->build_time = microtime(TRUE) - $start; + + // Attach displays + $this->attachDisplays(); + + // Let modules modify the view just after building it. + foreach (module_implements('views_post_build') as $module) { + $function = $module . '_views_post_build'; + $function($this); + } + + return TRUE; + } + + /** + * Internal method to build an individual set of handlers. + * + * @todo Some filter needs this function, even it is internal. + * + * @param string $key + * The type of handlers (filter etc.) which should be iterated over to + * build the relationship and query information. + */ + public function _build($key) { + $handlers = &$this->$key; + foreach ($handlers as $id => $data) { + + if (!empty($handlers[$id]) && is_object($handlers[$id])) { + $multiple_exposed_input = array(0 => NULL); + if ($handlers[$id]->multipleExposedInput()) { + $multiple_exposed_input = $handlers[$id]->group_multiple_exposed_input($this->exposed_data); + } + foreach ($multiple_exposed_input as $group_id) { + // Give this handler access to the exposed filter input. + if (!empty($this->exposed_data)) { + $converted = FALSE; + if ($handlers[$id]->isAGroup()) { + $converted = $handlers[$id]->convert_exposed_input($this->exposed_data, $group_id); + $handlers[$id]->store_group_input($this->exposed_data, $converted); + if (!$converted) { + continue; + } + } + $rc = $handlers[$id]->acceptExposedInput($this->exposed_data); + $handlers[$id]->storeExposedInput($this->exposed_data, $rc); + if (!$rc) { + continue; + } + } + $handlers[$id]->setRelationship(); + $handlers[$id]->query($this->display_handler->useGroupBy()); + } + } + } + } + + /** + * Execute the view's query. + * + * @param string $display_id + * The machine name of the display, which should be executed. + * + * @return bool + * Return whether the executing was successful, for example an argument + * could stop the process. + */ + public function execute($display_id = NULL) { + if (empty($this->built)) { + if (!$this->build($display_id)) { + return FALSE; + } + } + + if (!empty($this->executed)) { + return TRUE; + } + + // Don't allow to use deactivated displays, but display them on the live preview. + if (!$this->display_handler->isEnabled() && empty($this->live_preview)) { + $this->build_info['fail'] = TRUE; + return FALSE; + } + + // Let modules modify the view just prior to executing it. + foreach (module_implements('views_pre_execute') as $module) { + $function = $module . '_views_pre_execute'; + $function($this); + } + + // Check for already-cached results. + if (!empty($this->live_preview)) { + $cache = FALSE; + } + else { + $cache = $this->display_handler->getPlugin('cache'); + } + if ($cache && $cache->cache_get('results')) { + if ($this->pager->use_pager()) { + $this->pager->total_items = $this->total_rows; + $this->pager->update_page_info(); + } + } + else { + $this->query->execute($this); + // Enforce the array key rule as documented in + // views_plugin_query::execute(). + $this->result = array_values($this->result); + $this->_postExecute(); + if ($cache) { + $cache->cache_set('results'); + } + } + + // Let modules modify the view just after executing it. + foreach (module_implements('views_post_execute') as $module) { + $function = $module . '_views_post_execute'; + $function($this); + } + + $this->executed = TRUE; + } + + /** + * Render this view for a certain display. + * + * Note: You should better use just the preview function if you want to + * render a view. + * + * @param string $display_id + * The machine name of the display, which should be rendered. + * + * @return (string|NULL) + * Return the output of the rendered view or NULL if something failed in the process. + */ + public function render($display_id = NULL) { + $this->execute($display_id); + + // Check to see if the build failed. + if (!empty($this->build_info['fail'])) { + return; + } + if (!empty($this->build_info['denied'])) { + return; + } + + drupal_theme_initialize(); + $config = config('views.settings'); + + // Set the response so other parts can alter it. + $this->response = new Response('', 200); + + $start = microtime(TRUE); + if (!empty($this->live_preview) && $config->get('ui.show.additional_queries')) { + $this->startQueryCapture(); + } + + $exposed_form = $this->display_handler->getPlugin('exposed_form'); + $exposed_form->pre_render($this->result); + + // Check for already-cached output. + if (!empty($this->live_preview)) { + $cache = FALSE; + } + else { + $cache = $this->display_handler->getPlugin('cache'); + } + if ($cache && $cache->cache_get('output')) { + } + else { + if ($cache) { + $cache->cache_start(); + } + + // Run pre_render for the pager as it might change the result. + if (!empty($this->pager)) { + $this->pager->pre_render($this->result); + } + + // Initialize the style plugin. + $this->initStyle(); + + // Give field handlers the opportunity to perform additional queries + // using the entire resultset prior to rendering. + if ($this->style_plugin->usesFields()) { + foreach ($this->field as $id => $handler) { + if (!empty($this->field[$id])) { + $this->field[$id]->pre_render($this->result); + } + } + } + + $this->style_plugin->pre_render($this->result); + + // Let modules modify the view just prior to rendering it. + foreach (module_implements('views_pre_render') as $module) { + $function = $module . '_views_pre_render'; + $function($this); + } + + // Let the themes play too, because pre render is a very themey thing. + foreach ($GLOBALS['base_theme_info'] as $base) { + $function = $base->name . '_views_pre_render'; + if (function_exists($function)) { + $function($this); + } + } + $function = $GLOBALS['theme'] . '_views_pre_render'; + if (function_exists($function)) { + $function($this); + } + + $this->display_handler->output = $this->display_handler->render(); + if ($cache) { + $cache->cache_set('output'); + } + } + + $exposed_form->post_render($this->display_handler->output); + + if ($cache) { + $cache->post_render($this->display_handler->output); + } + + // Let modules modify the view output after it is rendered. + foreach (module_implements('views_post_render') as $module) { + $function = $module . '_views_post_render'; + $function($this, $this->display_handler->output, $cache); + } + + // Let the themes play too, because post render is a very themey thing. + foreach ($GLOBALS['base_theme_info'] as $base) { + $function = $base->name . '_views_post_render'; + if (function_exists($function)) { + $function($this); + } + } + $function = $GLOBALS['theme'] . '_views_post_render'; + if (function_exists($function)) { + $function($this, $this->display_handler->output, $cache); + } + + if (!empty($this->live_preview) && $config->get('ui.show.additional_queries')) { + $this->endQueryCapture(); + } + $this->render_time = microtime(TRUE) - $start; + + return $this->display_handler->output; + } + + /** + * Render a specific field via the field ID and the row # + * + * Note: You might want to use views_plugin_style::render_fields as it + * caches the output for you. + * + * @param string $field + * The id of the field to be rendered. + * + * @param int $row + * The row number in the $view->result which is used for the rendering. + * + * @return string + * The rendered output of the field. + */ + public function renderField($field, $row) { + if (isset($this->field[$field]) && isset($this->result[$row])) { + return $this->field[$field]->advanced_render($this->result[$row]); + } + } + + /** + * Execute the given display, with the given arguments. + * To be called externally by whatever mechanism invokes the view, + * such as a page callback, hook_block, etc. + * + * This function should NOT be used by anything external as this + * returns data in the format specified by the display. It can also + * have other side effects that are only intended for the 'proper' + * use of the display, such as setting page titles and breadcrumbs. + * + * If you simply want to view the display, use View::preview() instead. + */ + public function executeDisplay($display_id = NULL, $args = array()) { + if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) { + if (!$this->setDisplay($display_id)) { + return FALSE; + } + } + + $this->preExecute($args); + + // Execute the view + $output = $this->display_handler->execute(); + + $this->postExecute(); + return $output; + } + + /** + * Preview the given display, with the given arguments. + * + * To be called externally, probably by an AJAX handler of some flavor. + * Can also be called when views are embedded, as this guarantees + * normalized output. + */ + public function preview($display_id = NULL, $args = array()) { + if (empty($this->current_display) || ((!empty($display_id)) && $this->current_display != $display_id)) { + if (!$this->setDisplay($display_id)) { + return FALSE; + } + } + + $this->preview = TRUE; + $this->preExecute($args); + // Preview the view. + $output = $this->display_handler->preview(); + + $this->postExecute(); + return $output; + } + + /** + * Run attachments and let the display do what it needs to do prior + * to running. + */ + public function preExecute($args = array()) { + $this->old_view[] = views_get_current_view(); + views_set_current_view($this); + $display_id = $this->current_display; + + // Prepare the view with the information we have, but only if we were + // passed arguments, as they may have been set previously. + if ($args) { + $this->setArguments($args); + } + + // Let modules modify the view just prior to executing it. + foreach (module_implements('views_pre_view') as $module) { + $function = $module . '_views_pre_view'; + $function($this, $display_id, $this->args); + } + + // Allow hook_views_pre_view() to set the dom_id, then ensure it is set. + $this->dom_id = !empty($this->dom_id) ? $this->dom_id : md5($this->storage->name . REQUEST_TIME . rand()); + + // Allow the display handler to set up for execution + $this->display_handler->preExecute(); + } + + /** + * Unset the current view, mostly. + */ + public function postExecute() { + // unset current view so we can be properly destructed later on. + // Return the previous value in case we're an attachment. + + if ($this->old_view) { + $old_view = array_pop($this->old_view); + } + + views_set_current_view(isset($old_view) ? $old_view : FALSE); + } + + /** + * Run attachment displays for the view. + */ + public function attachDisplays() { + if (!empty($this->is_attachment)) { + return; + } + + if (!$this->display_handler->acceptAttachments()) { + return; + } + + $this->is_attachment = TRUE; + // Give other displays an opportunity to attach to the view. + foreach ($this->displayHandlers as $id => $display) { + if (!empty($this->displayHandlers[$id])) { + $this->displayHandlers[$id]->attachTo($this->current_display); + } + } + $this->is_attachment = FALSE; + } + + /** + * Called to get hook_menu() information from the view and the named display handler. + * + * @param $display_id + * A display id. + * @param $callbacks + * A menu callback array passed from views_menu_alter(). + */ + public function executeHookMenu($display_id = NULL, &$callbacks = array()) { + // Prepare the view with the information we have. + + // This was probably already called, but it's good to be safe. + if (!$this->setDisplay($display_id)) { + return FALSE; + } + + // Execute the view + if (isset($this->display_handler)) { + return $this->display_handler->executeHookMenu($callbacks); + } + } + + /** + * Called to get hook_block information from the view and the + * named display handler. + */ + public function executeHookBlockList($display_id = NULL) { + // Prepare the view with the information we have. + + // This was probably already called, but it's good to be safe. + if (!$this->setDisplay($display_id)) { + return FALSE; + } + + // Execute the view + if (isset($this->display_handler)) { + return $this->display_handler->executeHookBlockList(); + } + } + + /** + * Determine if the given user has access to the view. Note that + * this sets the display handler if it hasn't been. + */ + public function access($displays = NULL, $account = NULL) { + // Noone should have access to disabled views. + if (!$this->storage->isEnabled()) { + return FALSE; + } + + if (!isset($this->current_display)) { + $this->initDisplay(); + } + + if (!$account) { + $account = $GLOBALS['user']; + } + + // We can't use choose_display() here because that function + // calls this one. + $displays = (array)$displays; + foreach ($displays as $display_id) { + if (!empty($this->displayHandlers[$display_id])) { + if ($this->displayHandlers[$display_id]->access($account)) { + return TRUE; + } + } + } + + return FALSE; + } + + /** + * Sets the used response object of the view. + * + * @param Symfony\Component\HttpFoundation\Response $response + * The response object which should be set. + */ + public function setResponse(Response $response) { + $this->response = $response; + } + + /** + * Gets the response object used by the view. + * + * @return Symfony\Component\HttpFoundation\Response + * The response object of the view. + */ + public function getResponse() { + if (!isset($this->response)) { + $this->response = new Response(); + } + return $this->response; + } + + /** + * Get the view's current title. This can change depending upon how it + * was built. + */ + public function getTitle() { + if (empty($this->display_handler)) { + if (!$this->setDisplay('default')) { + return FALSE; + } + } + + // During building, we might find a title override. If so, use it. + if (!empty($this->build_info['title'])) { + $title = $this->build_info['title']; + } + else { + $title = $this->display_handler->getOption('title'); + } + + // Allow substitutions from the first row. + if ($this->initStyle()) { + $title = $this->style_plugin->tokenize_value($title, 0); + } + return $title; + } + + /** + * Override the view's current title. + * + * The tokens in the title get's replaced before rendering. + */ + public function setTitle($title) { + $this->build_info['title'] = $title; + return TRUE; + } + + /** + * Force the view to build a title. + */ + public function buildTitle() { + $this->initDisplay(); + + if (empty($this->built)) { + $this->initQuery(); + } + + $this->initHandlers(); + + $this->_buildArguments(); + } + + /** + * Get the URL for the current view. + * + * This URL will be adjusted for arguments. + */ + public function getUrl($args = NULL, $path = NULL) { + if (!empty($this->override_url)) { + return $this->override_url; + } + + if (!isset($path)) { + $path = $this->getPath(); + } + if (!isset($args)) { + $args = $this->args; + + // Exclude arguments that were computed, not passed on the URL. + $position = 0; + if (!empty($this->argument)) { + foreach ($this->argument as $argument_id => $argument) { + if (!empty($argument->is_default) && !empty($argument->options['default_argument_skip_url'])) { + unset($args[$position]); + } + $position++; + } + } + } + // Don't bother working if there's nothing to do: + if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) { + return $path; + } + + $pieces = array(); + $argument_keys = isset($this->argument) ? array_keys($this->argument) : array(); + $id = current($argument_keys); + foreach (explode('/', $path) as $piece) { + if ($piece != '%') { + $pieces[] = $piece; + } + else { + if (empty($args)) { + // Try to never put % in a url; use the wildcard instead. + if ($id && !empty($this->argument[$id]->options['exception']['value'])) { + $pieces[] = $this->argument[$id]->options['exception']['value']; + } + else { + $pieces[] = '*'; // gotta put something if there just isn't one. + } + + } + else { + $pieces[] = array_shift($args); + } + + if ($id) { + $id = next($argument_keys); + } + } + } + + if (!empty($args)) { + $pieces = array_merge($pieces, $args); + } + return implode('/', $pieces); + } + + /** + * Get the base path used for this view. + */ + public function getPath() { + if (!empty($this->override_path)) { + return $this->override_path; + } + + if (empty($this->display_handler)) { + if (!$this->setDisplay('default')) { + return FALSE; + } + } + return $this->display_handler->getPath(); + } + + /** + * Get the breadcrumb used for this view. + * + * @param $set + * If true, use drupal_set_breadcrumb() to install the breadcrumb. + */ + public function getBreadcrumb($set = FALSE) { + // Now that we've built the view, extract the breadcrumb. + $base = TRUE; + $breadcrumb = array(); + + if (!empty($this->build_info['breadcrumb'])) { + foreach ($this->build_info['breadcrumb'] as $path => $title) { + // Check to see if the frontpage is in the breadcrumb trail; if it + // is, we'll remove that from the actual breadcrumb later. + if ($path == config('system.site')->get('page.front')) { + $base = FALSE; + $title = t('Home'); + } + if ($title) { + $breadcrumb[] = l($title, $path, array('html' => TRUE)); + } + } + + if ($set) { + if ($base) { + $breadcrumb = array_merge(drupal_get_breadcrumb(), $breadcrumb); + } + drupal_set_breadcrumb($breadcrumb); + } + } + return $breadcrumb; + } + + /** + * Set up query capturing. + * + * db_query() stores the queries that it runs in global $queries, + * bit only if dev_query is set to true. In this case, we want + * to temporarily override that setting if it's not and we + * can do that without forcing a db rewrite by just manipulating + * $conf. This is kind of evil but it works. + */ + public function startQueryCapture() { + global $conf, $queries; + if (empty($conf['dev_query'])) { + $this->fix_dev_query = TRUE; + $conf['dev_query'] = TRUE; + } + + // Record the last query key used; anything already run isn't + // a query that we are interested in. + $this->last_query_key = NULL; + + if (!empty($queries)) { + $keys = array_keys($queries); + $this->last_query_key = array_pop($keys); + } + } + + /** + * Add the list of queries run during render to buildinfo. + * + * @see View::start_query_capture() + */ + public function endQueryCapture() { + global $conf, $queries; + if (!empty($this->fix_dev_query)) { + $conf['dev_query'] = FALSE; + } + + // make a copy of the array so we can manipulate it with array_splice. + $temp = $queries; + + // Scroll through the queries until we get to our last query key. + // Unset anything in our temp array. + if (isset($this->last_query_key)) { + while (list($id, $query) = each($queries)) { + if ($id == $this->last_query_key) { + break; + } + + unset($temp[$id]); + } + } + + $this->additional_queries = $temp; + } + + /** + * Overrides Drupal\entity\Entity::createDuplicate(). + * + * Makes a copy of this view that has been sanitized of handlers, any runtime + * data, ID, and UUID. + */ + public function createDuplicate() { + $data = config('views.view.' . $this->storage->id())->get(); + + // Reset the name and UUID. + unset($data['name']); + unset($data['uuid']); + + return entity_create('view', $data); + } + + /** + * Safely clone a view. + * + * This will completely wipe a view clean so it can be considered fresh. + * + * @return Drupal\views\ViewExecutable + * The cloned view. + */ + public function cloneView() { + $storage = clone $this->storage; + return $storage->getExecutable(TRUE); + } + + /** + * Unset references so that a $view object may be properly garbage + * collected. + */ + public function destroy() { + foreach (array_keys($this->displayHandlers) as $display_id) { + if (isset($this->displayHandlers[$display_id])) { + $this->displayHandlers[$display_id]->destroy(); + unset($this->displayHandlers[$display_id]); + } + } + + foreach ($this::viewsHandlerTypes() as $type => $info) { + if (isset($this->$type)) { + $handlers = &$this->$type; + foreach ($handlers as $id => $item) { + $handlers[$id]->destroy(); + } + unset($handlers); + } + } + + if (isset($this->style_plugin)) { + $this->style_plugin->destroy(); + } + + $keys = array('current_display', 'display_handler', 'displayHandlers', 'field', 'argument', 'filter', 'sort', 'relationship', 'header', 'footer', 'empty', 'query', 'result', 'inited', 'style_plugin', 'plugin_name', 'exposed_data', 'exposed_input', 'many_to_one_tables'); + foreach ($keys as $key) { + unset($this->$key); + } + + // These keys are checked by the next init, so instead of unsetting them, + // just set the default values. + $keys = array('items_per_page', 'offset', 'current_page'); + foreach ($keys as $key) { + if (isset($this->$key)) { + $this->$key = NULL; + } + } + + $this->built = $this->executed = FALSE; + $this->build_info = array(); + $this->attachment_before = ''; + $this->attachment_after = ''; + } + + /** + * Make sure the view is completely valid. + * + * @return + * TRUE if the view is valid; an array of error strings if it is not. + */ + public function validate() { + $this->initDisplay(); + + $errors = array(); + $this->display_errors = NULL; + + $current_display = $this->current_display; + foreach ($this->displayHandlers as $id => $display) { + if (!empty($display)) { + if (!empty($display->deleted)) { + continue; + } + + $result = $this->displayHandlers[$id]->validate(); + if (!empty($result) && is_array($result)) { + $errors = array_merge($errors, $result); + // Mark this display as having validation errors. + $this->display_errors[$id] = TRUE; + } + } + } + + $this->setDisplay($current_display); + return $errors ? $errors : TRUE; + } + + /** + * Provide a list of views handler types used in a view, with some information + * about them. + * + * @return array + * An array of associative arrays containing: + * - title: The title of the handler type. + * - ltitle: The lowercase title of the handler type. + * - stitle: A singular title of the handler type. + * - lstitle: A singular lowercase title of the handler type. + * - plural: Plural version of the handler type. + * - (optional) type: The actual internal used handler type. This key is + * just used for header,footer,empty to link to the internal type: area. + */ + public static function viewsHandlerTypes() { + static $retval = NULL; + + // Statically cache this so t() doesn't run a bajillion times. + if (!isset($retval)) { + $retval = array( + 'field' => array( + 'title' => t('Fields'), // title + 'ltitle' => t('fields'), // lowercase title for mid-sentence + 'stitle' => t('Field'), // singular title + 'lstitle' => t('field'), // singular lowercase title for mid sentence + 'plural' => 'fields', + ), + 'argument' => array( + 'title' => t('Contextual filters'), + 'ltitle' => t('contextual filters'), + 'stitle' => t('Contextual filter'), + 'lstitle' => t('contextual filter'), + 'plural' => 'arguments', + ), + 'sort' => array( + 'title' => t('Sort criteria'), + 'ltitle' => t('sort criteria'), + 'stitle' => t('Sort criterion'), + 'lstitle' => t('sort criterion'), + 'plural' => 'sorts', + ), + 'filter' => array( + 'title' => t('Filter criteria'), + 'ltitle' => t('filter criteria'), + 'stitle' => t('Filter criterion'), + 'lstitle' => t('filter criterion'), + 'plural' => 'filters', + ), + 'relationship' => array( + 'title' => t('Relationships'), + 'ltitle' => t('relationships'), + 'stitle' => t('Relationship'), + 'lstitle' => t('Relationship'), + 'plural' => 'relationships', + ), + 'header' => array( + 'title' => t('Header'), + 'ltitle' => t('header'), + 'stitle' => t('Header'), + 'lstitle' => t('Header'), + 'plural' => 'header', + 'type' => 'area', + ), + 'footer' => array( + 'title' => t('Footer'), + 'ltitle' => t('footer'), + 'stitle' => t('Footer'), + 'lstitle' => t('Footer'), + 'plural' => 'footer', + 'type' => 'area', + ), + 'empty' => array( + 'title' => t('No results behavior'), + 'ltitle' => t('no results behavior'), + 'stitle' => t('No results behavior'), + 'lstitle' => t('No results behavior'), + 'plural' => 'empty', + 'type' => 'area', + ), + ); + } + + return $retval; + } + + /** + * Returns the valid types of plugins that can be used. + * + * @return array + * An array of plugin type strings. + */ + public static function getPluginTypes() { + return array( + 'access', + 'area', + 'argument', + 'argument_default', + 'argument_validator', + 'cache', + 'display_extender', + 'display', + 'exposed_form', + 'field', + 'filter', + 'join', + 'pager', + 'query', + 'relationship', + 'row', + 'sort', + 'style', + 'wizard', + ); + } + + /** + * Adds an instance of a handler to the view. + * + * Items may be fields, filters, sort criteria, or arguments. + * + * @param string $display_id + * The machine name of the display. + * @param string $type + * The type of handler being added. + * @param string $table + * The name of the table this handler is from. + * @param string $field + * The name of the field this handler is from. + * @param array $options + * (optional) Extra options for this instance. Defaults to an empty array. + * @param string $id + * (optional) A unique ID for this handler instance. Defaults to NULL, in + * which case one will be generated. + * + * @return string + * The unique ID for this handler instance. + */ + public function addItem($display_id, $type, $table, $field, $options = array(), $id = NULL) { + $types = $this::viewsHandlerTypes(); + $this->setDisplay($display_id); + + $fields = $this->displayHandlers[$display_id]->getOption($types[$type]['plural']); + + if (empty($id)) { + $id = $this->generateItemId($field, $fields); + } + + // If the desired type is not found, use the original value directly. + $handler_type = !empty($types[$type]['type']) ? $types[$type]['type'] : $type; + + // @todo This variable is never used. + $handler = views_get_handler($table, $field, $handler_type); + + $fields[$id] = array( + 'id' => $id, + 'table' => $table, + 'field' => $field, + ) + $options; + + $this->displayHandlers[$display_id]->setOption($types[$type]['plural'], $fields); + + return $id; + } + + /** + * Generates a unique ID for an handler instance. + * + * These handler instances are typically fields, filters, sort criteria, or + * arguments. + * + * @param string $requested_id + * The requested ID for the handler instance. + * @param array $existing_items + * An array of existing handler instancess, keyed by their IDs. + * + * @return string + * A unique ID. This will be equal to $requested_id if no handler instance + * with that ID already exists. Otherwise, it will be appended with an + * integer to make it unique, e.g., "{$requested_id}_1", + * "{$requested_id}_2", etc. + */ + public static function generateItemId($requested_id, $existing_items) { + $count = 0; + $id = $requested_id; + while (!empty($existing_items[$id])) { + $id = $requested_id . '_' . ++$count; + } + return $id; + } + + /** + * Gets an array of handler instances for the current display. + * + * @param string $type + * The type of handlers to retrieve. + * @param string $display_id + * (optional) A specific display machine name to use. If NULL, the current + * display will be used. + * + * @return array + * An array of handler instances of a given type for this display. + */ + public function getItems($type, $display_id = NULL) { + $this->setDisplay($display_id); + + if (!isset($display_id)) { + $display_id = $this->current_display; + } + + // Get info about the types so we can get the right data. + $types = $this::viewsHandlerTypes(); + return $this->displayHandlers[$display_id]->getOption($types[$type]['plural']); + } + + /** + * Gets the configuration of a handler instance on a given display. + * + * @param string $display_id + * The machine name of the display. + * @param string $type + * The type of handler to retrieve. + * @param string $id + * The ID of the handler to retrieve. + * + * @return array|null + * Either the handler instance's configuration, or NULL if the handler is + * not used on the display. + */ + public function getItem($display_id, $type, $id) { + // Get info about the types so we can get the right data. + $types = $this::viewsHandlerTypes(); + // Initialize the display + $this->setDisplay($display_id); + + // Get the existing configuration + $fields = $this->displayHandlers[$display_id]->getOption($types[$type]['plural']); + + return isset($fields[$id]) ? $fields[$id] : NULL; + } + + /** + * Sets the configuration of a handler instance on a given display. + * + * @param string $display_id + * The machine name of the display. + * @param string $type + * The type of handler being set. + * @param string $id + * The ID of the handler being set. + * @param array|null $item + * An array of configuration for a handler, or NULL to remove this instance. + * + * @see set_item_option() + */ + public function setItem($display_id, $type, $id, $item) { + // Get info about the types so we can get the right data. + $types = $this::viewsHandlerTypes(); + // Initialize the display. + $this->setDisplay($display_id); + + // Get the existing configuration. + $fields = $this->displayHandlers[$display_id]->getOption($types[$type]['plural']); + if (isset($item)) { + $fields[$id] = $item; + } + else { + unset($fields[$id]); + } + + // Store. + $this->displayHandlers[$display_id]->setOption($types[$type]['plural'], $fields); + } + + /** + * Sets an option on a handler instance. + * + * Use this only if you have just 1 or 2 options to set; if you have many, + * consider getting the handler instance, adding the options and using + * set_item() directly. + * + * @param string $display_id + * The machine name of the display. + * @param string $type + * The type of handler being set. + * @param string $id + * The ID of the handler being set. + * @param string $option + * The configuration key for the value being set. + * @param mixed $value + * The value being set. + * + * @see set_item() + */ + public function setItemOption($display_id, $type, $id, $option, $value) { + $item = $this->getItem($display_id, $type, $id); + $item[$option] = $value; + $this->setItem($display_id, $type, $id, $item); + } + + /** + * Creates and stores a new display. + * + * @param string $id + * The ID for the display being added. + * + * @return Drupal\views\Plugin\views\display\DisplayPluginBase + * A reference to the new handler object. + */ + public function &newDisplay($id) { + // Create a handler. + $this->displayHandlers[$id] = views_get_plugin('display', $this->storage->display[$id]['display_plugin']); + if (empty($this->displayHandlers[$id])) { + // provide a 'default' handler as an emergency. This won't work well but + // it will keep things from crashing. + $this->displayHandlers[$id] = views_get_plugin('display', 'default'); + } + + if (!empty($this->displayHandlers[$id])) { + // Initialize the new display handler with data. + $this->displayHandlers[$id]->init($this, $this->storage->display[$id]); + // If this is NOT the default display handler, let it know which is + if ($id != 'default') { + // @todo is the '&' still required in php5? + $this->displayHandlers[$id]->default_display = &$this->displayHandlers['default']; + } + } + + return $this->displayHandlers[$id]; + } + +} diff --git a/core/modules/views/lib/Drupal/views/ViewStorage.php b/core/modules/views/lib/Drupal/views/ViewStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..d4ae36c765e74a8d1727f2d0eb7b592831b50b4e --- /dev/null +++ b/core/modules/views/lib/Drupal/views/ViewStorage.php @@ -0,0 +1,380 @@ +executable = $executable; + } + + /** + * Retrieves the executable version of this view. + * + * @param bool $reset + * Get a new Drupal\views\ViewExecutable instance. + * @param bool $ui + * If this should return Drupal\views_ui\ViewUI instead. + * + * @return Drupal\views\ViewExecutable + * The executable version of this view. + */ + public function getExecutable($reset = FALSE, $ui = FALSE) { + if (!isset($this->executable) || $reset) { + // @todo Remove this approach and use proper dependency injection. + if ($ui) { + $executable = new ViewUI($this); + } + else { + $executable = new ViewExecutable($this); + } + $this->setExecutable($executable); + } + return $this->executable; + } + + /** + * Initializes the display. + * + * @todo Inspect calls to this and attempt to clean up. + * @see Drupal\views\ViewExecutable::initDisplay() + */ + public function initDisplay() { + $this->getExecutable()->initDisplay(); + } + + /** + * Returns the name of the module implementing this view. + */ + public function getModule() { + return $this->module; + } + + /** + * Overrides Drupal\Core\Entity\EntityInterface::uri(). + */ + public function uri() { + $info = $this->entityInfo(); + return array( + 'path' => $info['list path'], + ); + } + + /** + * Overrides Drupal\Core\Entity\EntityInterface::id(). + */ + public function id() { + return $this->name; + } + + /** + * Implements Drupal\views\ViewStorageInterface::enable(). + */ + public function enable() { + $this->disabled = FALSE; + $this->save(); + } + + /** + * Implements Drupal\views\ViewStorageInterface::disable(). + */ + public function disable() { + $this->disabled = TRUE; + $this->save(); + } + + /** + * Implements Drupal\views\ViewStorageInterface::isEnabled(). + */ + public function isEnabled() { + return !$this->disabled; + } + + /** + * Return the human readable name for a view. + * + * When a certain view doesn't have a human readable name return the machine readable name. + */ + public function getHumanName() { + if (!empty($this->human_name)) { + $human_name = $this->human_name; + } + else { + $human_name = $this->name; + } + return $human_name; + } + + /** + * Adds a new display handler to the view, automatically creating an ID. + * + * @param string $plugin_id + * (optional) The plugin type from the Views plugin annotation. Defaults to + * 'page'. + * @param string $title + * (optional) The title of the display. Defaults to NULL. + * @param string $id + * (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults + * to NULL. + * + * @return string|false + * The key to the display in $view->display, or FALSE if no plugin ID was + * provided. + */ + public function addDisplay($plugin_id = 'page', $title = NULL, $id = NULL) { + if (empty($plugin_id)) { + return FALSE; + } + + $plugin = views_get_plugin_definition('display', $plugin_id); + if (empty($plugin)) { + $plugin['title'] = t('Broken'); + } + + if (empty($id)) { + $id = $this->generateDisplayId($plugin_id); + + // Generate a unique human-readable name by inspecting the counter at the + // end of the previous display ID, e.g., 'page_1'. + if ($id !== 'default') { + preg_match("/[0-9]+/", $id, $count); + $count = $count[0]; + } + else { + $count = ''; + } + + if (empty($title)) { + // If there is no title provided, use the plugin title, and if there are + // multiple displays, append the count. + $title = $plugin['title']; + if ($count > 1) { + $title .= ' ' . $count; + } + } + } + + $display_options = array( + 'display_plugin' => $plugin_id, + 'id' => $id, + 'display_title' => $title, + 'position' => NULL, + 'display_options' => array(), + ); + + // Add the display options to the view. + $this->display[$id] = $display_options; + return $id; + } + + /** + * Generates a display ID of a certain plugin type. + * + * @param string $plugin_id + * Which plugin should be used for the new display ID. + */ + protected function generateDisplayId($plugin_id) { + // 'default' is singular and is unique, so just go with 'default' + // for it. For all others, start counting. + if ($plugin_id == 'default') { + return 'default'; + } + // Initial ID. + $id = $plugin_id . '_1'; + $count = 1; + + // Loop through IDs based upon our style plugin name until + // we find one that is unused. + while (!empty($this->display[$id])) { + $id = $plugin_id . '_' . ++$count; + } + + return $id; + } + + /** + * Creates a new display and a display handler for it. + * + * @param string $plugin_id + * (optional) The plugin type from the Views plugin annotation. Defaults to + * 'page'. + * @param string $title + * (optional) The title of the display. Defaults to NULL. + * @param string $id + * (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults + * to NULL. + * + * @return Drupal\views\Plugin\views\display\DisplayPluginBase + * A reference to the new handler object. + */ + public function &newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) { + $id = $this->addDisplay($plugin_id, $title, $id); + return $this->getExecutable()->newDisplay($id); + } + + /** + * Gets a list of displays included in the view. + * + * @return array + * An array of display types that this view includes. + */ + function getDisplaysList() { + $manager = drupal_container()->get('plugin.manager.views.display'); + $displays = array(); + foreach ($this->display as $display) { + $definition = $manager->getDefinition($display['display_plugin']); + if (!empty($definition['admin'])) { + $displays[$definition['admin']] = TRUE; + } + } + + ksort($displays); + return array_keys($displays); + } + + /** + * Gets a list of paths assigned to the view. + * + * @return array + * An array of paths for this view. + */ + public function getPaths() { + $all_paths = array(); + if (empty($this->display)) { + $all_paths[] = t('Edit this view to add a display.'); + } + else { + foreach ($this->display as $display) { + if (!empty($display['display_options']['path'])) { + $path = $display['display_options']['path']; + if ($this->isEnabled() && strpos($path, '%') === FALSE) { + $all_paths[] = l('/' . $path, $path); + } + else { + $all_paths[] = check_plain('/' . $path); + } + } + } + } + + return array_unique($all_paths); + } + +} diff --git a/core/modules/views/lib/Drupal/views/ViewStorageController.php b/core/modules/views/lib/Drupal/views/ViewStorageController.php new file mode 100644 index 0000000000000000000000000000000000000000..007b9ff81a231f77b1a88da9bf0d7c7ec114dc34 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/ViewStorageController.php @@ -0,0 +1,116 @@ +getModule())) { + return TRUE; + } + return FALSE; + }); + } + + /** + * Overrides Drupal\config\ConfigStorageController::attachLoad(); + */ + protected function attachLoad(&$queried_entities, $revision_id = FALSE) { + foreach ($queried_entities as $id => $entity) { + // Create a uuid if we don't have one. + if (empty($entity->{$this->uuidKey})) { + // Only get an instance of uuid once. + if (!isset($this->uuidFactory)) { + $this->uuidFactory = new Uuid(); + } + $entity->{$this->uuidKey} = $this->uuidFactory->generate(); + } + $this->attachDisplays($entity); + } + + parent::attachLoad($queried_entities, $revision_id); + } + + /** + * Overrides Drupal\config\ConfigStorageController::postSave(). + */ + public function postSave(EntityInterface $entity, $update) { + parent::postSave($entity, $update); + // Clear caches. + views_invalidate_cache(); + } + + /** + * Overrides Drupal\config\ConfigStorageController::create(). + */ + public function create(array $values) { + // If there is no information about displays available add at least the + // default display. + $values += array( + 'display' => array( + 'default' => array( + 'display_plugin' => 'default', + 'id' => 'default', + 'display_title' => 'Master', + ), + ) + ); + + $entity = parent::create($values); + + $this->attachDisplays($entity); + return $entity; + } + + /** + * Add defaults to the display options. + * + * @param Drupal\Core\Entity\EntityInterface $entity + */ + protected function attachDisplays(EntityInterface $entity) { + if (isset($entity->display) && is_array($entity->display)) { + $displays = array(); + + foreach ($entity->get('display') as $key => $options) { + $options += array( + 'display_options' => array(), + 'display_plugin' => NULL, + 'id' => NULL, + 'display_title' => '', + 'position' => NULL, + ); + // Add the defaults for the display. + $displays[$key] = $options; + } + + $entity->set('display', $displays); + } + } + +} diff --git a/core/modules/views/lib/Drupal/views/ViewStorageInterface.php b/core/modules/views/lib/Drupal/views/ViewStorageInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..b29fa03136cccfab8069f3ed99fdb6f1266a644f --- /dev/null +++ b/core/modules/views/lib/Drupal/views/ViewStorageInterface.php @@ -0,0 +1,34 @@ +register('plugin.manager.views.join', 'Drupal\views\Plugin\Type\JoinManager'); + } + elseif ($type == 'wizard') { + $container->register('plugin.manager.views.wizard', 'Drupal\views\Plugin\Type\WizardManager'); + } + else { + $container->register("plugin.manager.views.$type", 'Drupal\views\Plugin\Type\PluginManager') + ->addArgument($type); + } + } + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/argument/CategoryCid.php b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/CategoryCid.php new file mode 100644 index 0000000000000000000000000000000000000000..2be3fdd438e550e3d85dc2108929f7850b6560e1 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/CategoryCid.php @@ -0,0 +1,41 @@ +addField('c', 'title'); + $query->condition('c.cid', $this->value); + $result = $query->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->title); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Fid.php b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Fid.php new file mode 100644 index 0000000000000000000000000000000000000000..2561098e6df1bbfda10281d8abe01db34b9a0c2a --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Fid.php @@ -0,0 +1,42 @@ + $this->value)); + $query = db_select('aggregator_feed', 'f'); + $query->addField('f', 'title'); + $query->condition('f.fid', $this->value); + $result = $query->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->title); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Iid.php b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Iid.php new file mode 100644 index 0000000000000000000000000000000000000000..c97b6c412d29f4c9abc2b3a5734be93db0b75a91 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/argument/Iid.php @@ -0,0 +1,42 @@ +value), '%d')); + + $result = db_select('aggregator_item') + ->condition('iid', $this->value, 'IN') + ->fields(array('title')) + ->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->title); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/field/Category.php b/core/modules/views/lib/Views/aggregator/Plugin/views/field/Category.php new file mode 100644 index 0000000000000000000000000000000000000000..42632137289d6e96510f0a4309cf35e4b7c701a2 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/field/Category.php @@ -0,0 +1,74 @@ +additional_fields['cid'] = 'cid'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_category'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to category option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_category'] = array( + '#title' => t('Link this field to its aggregator category page'), + '#description' => t('This will override any other link you have set.'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_category']), + ); + parent::buildOptionsForm($form, $form_state); + } + + /** + * Render whatever the data is as a link to the category. + * + * Data should be made XSS safe prior to calling this function. + */ + function render_link($data, $values) { + $cid = $this->get_value($values, 'cid'); + if (!empty($this->options['link_to_category']) && !empty($cid) && $data !== NULL && $data !== '') { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "aggregator/category/$cid"; + } + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/field/TitleLink.php b/core/modules/views/lib/Views/aggregator/Plugin/views/field/TitleLink.php new file mode 100644 index 0000000000000000000000000000000000000000..5f3616d09873584ce9307fa3d934557248e5fcb5 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/field/TitleLink.php @@ -0,0 +1,72 @@ +additional_fields['link'] = 'link'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['display_as_link'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + /** + * Provide link to the page being visited. + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['display_as_link'] = array( + '#title' => t('Display as link'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['display_as_link']), + ); + parent::buildOptionsForm($form, $form_state); + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + + function render_link($data, $values) { + $link = $this->get_value($values, 'link'); + if (!empty($this->options['display_as_link'])) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = $link; + $this->options['alter']['html'] = TRUE; + } + + return $data; + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/field/Xss.php b/core/modules/views/lib/Views/aggregator/Plugin/views/field/Xss.php new file mode 100644 index 0000000000000000000000000000000000000000..4cea7aaf19eba224865a77980628c6878e3fbe41 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/field/Xss.php @@ -0,0 +1,30 @@ +get_value($values); + return aggregator_filter_xss($value); + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/filter/CategoryCid.php b/core/modules/views/lib/Views/aggregator/Plugin/views/filter/CategoryCid.php new file mode 100644 index 0000000000000000000000000000000000000000..c676ba48587412ad610a8ae731a6e9c6b0f3919b --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/filter/CategoryCid.php @@ -0,0 +1,39 @@ +value_options)) { + return; + } + + $this->value_options = array(); + // Uses db_query() rather than db_select() because the query is static and + // does not include any variables. + $result = db_query('SELECT * FROM {aggregator_category} ORDER BY title'); + foreach ($result as $category) { + $this->value_options[$category->cid] = $category->title; + } + } + +} diff --git a/core/modules/views/lib/Views/aggregator/Plugin/views/row/Rss.php b/core/modules/views/lib/Views/aggregator/Plugin/views/row/Rss.php new file mode 100644 index 0000000000000000000000000000000000000000..4a5b6590faf1093c5d2a67a948b0f9828e29f178 --- /dev/null +++ b/core/modules/views/lib/Views/aggregator/Plugin/views/row/Rss.php @@ -0,0 +1,92 @@ + 'default'); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['item_length'] = array( + '#type' => 'select', + '#title' => t('Display type'), + '#options' => array( + 'fulltext' => t('Full text'), + 'teaser' => t('Title plus teaser'), + 'title' => t('Title only'), + 'default' => t('Use default RSS settings'), + ), + '#default_value' => $this->options['item_length'], + ); + } + + function render($row) { + $iid = $row->{$this->field_alias}; + $query = db_select('aggregator_item', 'ai'); + $query->leftJoin('aggregator_feed', 'af', 'ai.fid = af.fid'); + $query->fields('ai'); + $query->addExpression('af.title', 'feed_title'); + $query->addExpression('ai.link', 'feed_LINK'); + $query->condition('iid', $iid); + $result = $query->execute(); + + $item->elements = array( + array( + 'key' => 'pubDate', + 'value' => gmdate('r', $item->timestamp), + ), + array( + 'key' => 'dc:creator', + 'value' => $item->author, + ), + array( + 'key' => 'guid', + 'value' => $item->guid, + 'attributes' => array('isPermaLink' => 'false') + ), + ); + + foreach ($item->elements as $element) { + if (isset($element['namespace'])) { + $this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $element['namespace']); + } + } + + return theme($this->themeFunctions(), array( + 'view' => $this->view, + 'options' => $this->options, + 'row' => $item + )); + } + +} diff --git a/core/modules/views/lib/Views/block/Plugin/views/display/Block.php b/core/modules/views/lib/Views/block/Plugin/views/display/Block.php new file mode 100644 index 0000000000000000000000000000000000000000..a1d205aff4cec39334c358299cf6a99e42447bff --- /dev/null +++ b/core/modules/views/lib/Views/block/Plugin/views/display/Block.php @@ -0,0 +1,270 @@ + '', 'translatable' => TRUE); + $options['block_caching'] = array('default' => DRUPAL_NO_CACHE); + + return $options; + } + + /** + * The default block handler doesn't support configurable items, + * but extended block handlers might be able to do interesting + * stuff with it. + */ + public function executeHookBlockList($delta = 0, $edit = array()) { + $delta = $this->view->storage->name . '-' . $this->display['id']; + $desc = $this->getOption('block_description'); + + if (empty($desc)) { + if ($this->display['display_title'] == $this->definition['title']) { + $desc = t('View: !view', array('!view' => $this->view->storage->getHumanName())); + } + else { + $desc = t('View: !view: !display', array('!view' => $this->view->storage->getHumanName(), '!display' => $this->display['display_title'])); + } + } + return array( + $delta => array( + 'info' => $desc, + 'cache' => $this->getCacheType() + ), + ); + } + + /** + * The display block handler returns the structure necessary for a block. + */ + public function execute() { + // Prior to this being called, the $view should already be set to this + // display, and arguments should be set on the view. + $info['content'] = $this->view->render(); + $info['subject'] = filter_xss_admin($this->view->getTitle()); + if (!empty($this->view->result) || $this->getOption('empty') || !empty($this->view->style_plugin->definition['even empty'])) { + return $info; + } + } + + /** + * Provide the summary for page options in the views UI. + * + * This output is returned as an array. + */ + public function optionsSummary(&$categories, &$options) { + // It is very important to call the parent function here: + parent::optionsSummary($categories, $options); + + $categories['block'] = array( + 'title' => t('Block settings'), + 'column' => 'second', + 'build' => array( + '#weight' => -10, + ), + ); + + $block_description = strip_tags($this->getOption('block_description')); + if (empty($block_description)) { + $block_description = t('None'); + } + + $options['block_description'] = array( + 'category' => 'block', + 'title' => t('Block name'), + 'value' => views_ui_truncate($block_description, 24), + ); + + $types = $this->blockCachingModes(); + $options['block_caching'] = array( + 'category' => 'other', + 'title' => t('Block caching'), + 'value' => $types[$this->getCacheType()], + ); + } + + /** + * Provide a list of core's block caching modes. + */ + protected function blockCachingModes() { + return array( + DRUPAL_NO_CACHE => t('Do not cache'), + DRUPAL_CACHE_GLOBAL => t('Cache once for everything (global)'), + DRUPAL_CACHE_PER_PAGE => t('Per page'), + DRUPAL_CACHE_PER_ROLE => t('Per role'), + DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE => t('Per role per page'), + DRUPAL_CACHE_PER_USER => t('Per user'), + DRUPAL_CACHE_PER_USER | DRUPAL_CACHE_PER_PAGE => t('Per user per page'), + ); + } + + /** + * Provide a single method to figure caching type, keeping a sensible default + * for when it's unset. + */ + protected function getCacheType() { + $cache_type = $this->getOption('block_caching'); + if (empty($cache_type)) { + $cache_type = DRUPAL_NO_CACHE; + } + return $cache_type; + } + + /** + * Provide the default form for setting options. + */ + public function buildOptionsForm(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::buildOptionsForm($form, $form_state); + + switch ($form_state['section']) { + case 'block_description': + $form['#title'] .= t('Block admin description'); + $form['block_description'] = array( + '#type' => 'textfield', + '#description' => t('This will appear as the name of this block in administer >> structure >> blocks.'), + '#default_value' => $this->getOption('block_description'), + ); + break; + case 'block_caching': + $form['#title'] .= t('Block caching type'); + + $form['block_caching'] = array( + '#type' => 'radios', + '#description' => t("This sets the default status for Drupal's built-in block caching method; this requires that caching be turned on in block administration, and be careful because you have little control over when this cache is flushed."), + '#options' => $this->blockCachingModes(), + '#default_value' => $this->getCacheType(), + ); + break; + case 'exposed_form_options': + $this->view->initHandlers(); + if (!$this->usesExposed() && parent::usesExposed()) { + $form['exposed_form_options']['warning'] = array( + '#weight' => -10, + '#markup' => ' ', + ); + } + } + } + + /** + * Perform any necessary changes to the form values prior to storage. + * There is no need for this function to actually store the data. + */ + public function submitOptionsForm(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::submitOptionsForm($form, $form_state); + switch ($form_state['section']) { + case 'display_id': + $this->updateBlockBid($form_state['view']->storage->name, $this->display['id'], $this->display['new_id']); + break; + case 'block_description': + $this->setOption('block_description', $form_state['values']['block_description']); + break; + case 'block_caching': + $this->setOption('block_caching', $form_state['values']['block_caching']); + $this->saveBlockCache($form_state['view']->storage->name . '-'. $form_state['display_id'], $form_state['values']['block_caching']); + break; + } + } + + /** + * Block views use exposed widgets only if AJAX is set. + */ + public function usesExposed() { + if ($this->isAJAXEnabled()) { + return parent::usesExposed(); + } + return FALSE; + } + + /** + * Update the block delta when you change the machine readable name of the display. + */ + protected function updateBlockBid($name, $old_delta, $delta) { + $old_hashes = $hashes = variable_get('views_block_hashes', array()); + + $old_delta = $name . '-' . $old_delta; + $delta = $name . '-' . $delta; + if (strlen($old_delta) >= 32) { + $old_delta = md5($old_delta); + unset($hashes[$old_delta]); + } + if (strlen($delta) >= 32) { + $md5_delta = md5($delta); + $hashes[$md5_delta] = $delta; + $delta = $md5_delta; + } + + // Maybe people don't have block module installed, so let's skip this. + if (db_table_exists('block')) { + db_update('block') + ->fields(array('delta' => $delta)) + ->condition('delta', $old_delta) + ->execute(); + } + + // Update the hashes if needed. + if ($hashes != $old_hashes) { + variable_set('views_block_hashes', $hashes); + } + } + + /** + * Save the block cache setting in the blocks table if this block allready + * exists in the blocks table. Dirty fix untill http://drupal.org/node/235673 gets in. + */ + protected function saveBlockCache($delta, $cache_setting) { + if (strlen($delta) >= 32) { + $delta = md5($delta); + } + if (db_table_exists('block') && $bid = db_query("SELECT bid FROM {block} WHERE module = 'views' AND delta = :delta", array( + ':delta' => $delta))->fetchField()) { + db_update('block') + ->fields(array( + 'cache' => $cache_setting, + )) + ->condition('module', 'views') + ->condition('delta', $delta) + ->execute(); + } + } + +} diff --git a/core/modules/views/lib/Views/book/Plugin/views/argument_default/Root.php b/core/modules/views/lib/Views/book/Plugin/views/argument_default/Root.php new file mode 100644 index 0000000000000000000000000000000000000000..9fa8e4fea41042e9c9ca6dbd39f73f791ef42087 --- /dev/null +++ b/core/modules/views/lib/Views/book/Plugin/views/argument_default/Root.php @@ -0,0 +1,36 @@ +book['bid'])) { + return $node->book['bid']; + } + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/argument/UserUid.php b/core/modules/views/lib/Views/comment/Plugin/views/argument/UserUid.php new file mode 100644 index 0000000000000000000000000000000000000000..d11d011ea34fa2ac8d590a071a105e3abc4694b3 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/argument/UserUid.php @@ -0,0 +1,76 @@ +argument) { + $title = config('user.settings')->get('anonymous'); + } + else { + $query = db_select('users', 'u'); + $query->addField('u', 'name'); + $query->condition('u.uid', $this->argument); + $title = $query->execute()->fetchField(); + } + if (empty($title)) { + return t('No user'); + } + + return check_plain($title); + } + + function default_actions($which = NULL) { + // Disallow summary views on this argument. + if (!$which) { + $actions = parent::default_actions(); + unset($actions['summary asc']); + unset($actions['summary desc']); + return $actions; + } + + if ($which != 'summary asc' && $which != 'summary desc') { + return parent::default_actions($which); + } + } + + public function query($group_by = FALSE) { + $this->ensureMyTable(); + + $subselect = db_select('comment', 'c'); + $subselect->addField('c', 'cid'); + $subselect->condition('c.uid', $this->argument); + $subselect->where("c.nid = $this->tableAlias.nid"); + + $condition = db_or() + ->condition("$this->tableAlias.uid", $this->argument, '=') + ->exists($subselect); + + $this->query->add_where(0, $condition); + } + + function get_sort_name() { + return t('Numerical', array(), array('context' => 'Sort order')); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/Comment.php b/core/modules/views/lib/Views/comment/Plugin/views/field/Comment.php new file mode 100644 index 0000000000000000000000000000000000000000..fe151ad462887a167abe4b0c390da71229015fdf --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/Comment.php @@ -0,0 +1,86 @@ +options['link_to_comment'])) { + $this->additional_fields['cid'] = 'cid'; + $this->additional_fields['nid'] = 'nid'; + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_comment'] = array('default' => TRUE, 'bool' => TRUE); + $options['link_to_node'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + /** + * Provide link-to-comment option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_comment'] = array( + '#title' => t('Link this field to its comment'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_comment'], + ); + $form['link_to_node'] = array( + '#title' => t('Link field to the node if there is no comment.'), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_node'], + ); + parent::buildOptionsForm($form, $form_state); + } + + function render_link($data, $values) { + if (!empty($this->options['link_to_comment'])) { + $this->options['alter']['make_link'] = TRUE; + $nid = $this->get_value($values, 'nid'); + $cid = $this->get_value($values, 'cid'); + if (!empty($cid)) { + $this->options['alter']['path'] = "comment/" . $cid; + $this->options['alter']['fragment'] = "comment-" . $cid; + } + // If there is no comment link to the node. + elseif ($this->options['link_to_node']) { + $this->options['alter']['path'] = "node/" . $nid; + } + } + + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/Depth.php b/core/modules/views/lib/Views/comment/Plugin/views/field/Depth.php new file mode 100644 index 0000000000000000000000000000000000000000..5d0224895c8c12b0d98c91373a8667132401335c --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/Depth.php @@ -0,0 +1,33 @@ +get_value($values); + return count(explode('.', $comment_thread)) - 1; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/LastTimestamp.php b/core/modules/views/lib/Views/comment/Plugin/views/field/LastTimestamp.php new file mode 100644 index 0000000000000000000000000000000000000000..e6319532d18f1a82445c590450a589120d54068d --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/LastTimestamp.php @@ -0,0 +1,45 @@ +additional_fields['comment_count'] = 'comment_count'; + } + + function render($values) { + $comment_count = $this->get_value($values, 'comment_count'); + if (empty($this->options['empty_zero']) || $comment_count) { + return parent::render($values); + } + else { + return NULL; + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/Link.php b/core/modules/views/lib/Views/comment/Plugin/views/field/Link.php new file mode 100644 index 0000000000000000000000000000000000000000..6ba3c8eb1ec7d70e43fe13e14cb688a33c5c5abb --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/Link.php @@ -0,0 +1,74 @@ + '', 'translatable' => TRUE); + $options['link_to_node'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + $form['link_to_node'] = array( + '#title' => t('Link field to the node if there is no comment.'), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_node'], + ); + parent::buildOptionsForm($form, $form_state); + } + + public function query() {} + + function render($values) { + $comment = $this->get_entity($values); + return $this->render_link($comment, $values); + } + + function render_link($data, $values) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('view'); + $comment = $data; + $nid = $comment->nid; + $cid = $comment->id(); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['html'] = TRUE; + + if (!empty($cid)) { + $this->options['alter']['path'] = "comment/" . $cid; + $this->options['alter']['fragment'] = "comment-" . $cid; + } + // If there is no comment link to the node. + elseif ($this->options['link_to_node']) { + $this->options['alter']['path'] = "node/" . $nid; + } + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/LinkApprove.php b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkApprove.php new file mode 100644 index 0000000000000000000000000000000000000000..6bdb3c09f820555b5d21b290737dc406e2260298 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkApprove.php @@ -0,0 +1,47 @@ +get_value($values, 'status'); + + // Don't show an approve link on published nodes. + if ($status == COMMENT_PUBLISHED) { + return; + } + + $text = !empty($this->options['text']) ? $this->options['text'] : t('approve'); + $cid = $this->get_value($values, 'cid'); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "comment/" . $cid . "/approve"; + $this->options['alter']['query'] = drupal_get_destination() + array('token' => drupal_get_token("comment/$cid/approve")); + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/LinkDelete.php b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkDelete.php new file mode 100644 index 0000000000000000000000000000000000000000..a445a6f957367e621dbfd8fa2d5fb900741bf3bb --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkDelete.php @@ -0,0 +1,40 @@ +options['text']) ? $this->options['text'] : t('delete'); + $cid = $this->get_value($values, 'cid'); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "comment/" . $cid . "/delete"; + $this->options['alter']['query'] = drupal_get_destination(); + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/LinkEdit.php b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkEdit.php new file mode 100644 index 0000000000000000000000000000000000000000..7b8c1f246742f85c88b9a5db005dac43b945ca70 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkEdit.php @@ -0,0 +1,63 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['destination'] = array( + '#type' => 'checkbox', + '#title' => t('Use destination'), + '#description' => t('Add destination to the link'), + '#default_value' => $this->options['destination'], + '#fieldset' => 'more', + ); + } + + function render_link($data, $values) { + parent::render_link($data, $values); + // ensure user has access to edit this comment. + $comment = $this->get_value($values); + if (!comment_access('edit', $comment)) { + return; + } + + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + unset($this->options['alter']['fragment']); + + if (!empty($this->options['destination'])) { + $this->options['alter']['query'] = drupal_get_destination(); + } + + $this->options['alter']['path'] = "comment/" . $comment->id() . "/edit"; + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/LinkReply.php b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkReply.php new file mode 100644 index 0000000000000000000000000000000000000000..8ddfd79ecb3332799820244170d547c3c03d64b6 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/LinkReply.php @@ -0,0 +1,40 @@ +options['text']) ? $this->options['text'] : t('reply'); + $nid = $this->get_value($values, 'nid'); + $cid = $this->get_value($values, 'cid'); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "comment/reply/" . $nid . '/' . $cid; + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastCommentName.php b/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastCommentName.php new file mode 100644 index 0000000000000000000000000000000000000000..8ae5078130487faf975ad68ba767334f7097a61b --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastCommentName.php @@ -0,0 +1,77 @@ +ensureMyTable(); + // join 'users' to this table via vid + $definition = array( + 'table' => 'users', + 'field' => 'uid', + 'left_table' => $this->tableAlias, + 'left_field' => 'last_comment_uid', + 'extra' => array( + array( + 'field' => 'uid', + 'operator' => '!=', + 'value' => '0' + ) + ) + ); + $join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition); + + // ncs_user alias so this can work with the sort handler, below. +// $this->user_table = $this->query->add_relationship(NULL, $join, 'users', $this->relationship); + $this->user_table = $this->query->ensure_table('ncs_users', $this->relationship, $join); + + $this->field_alias = $this->query->add_field(NULL, "COALESCE($this->user_table.name, $this->tableAlias.$this->field)", $this->tableAlias . '_' . $this->field); + + $this->user_field = $this->query->add_field($this->user_table, 'name'); + $this->uid = $this->query->add_field($this->tableAlias, 'last_comment_uid'); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['link_to_user'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + function render($values) { + if (!empty($this->options['link_to_user'])) { + $account = entity_create('user', array()); + $account->name = $this->get_value($values); + $account->uid = $values->{$this->uid}; + return theme('username', array( + 'account' => $account + )); + } + else { + return $this->sanitizeValue($this->get_value($values)); + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastUpdated.php b/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastUpdated.php new file mode 100644 index 0000000000000000000000000000000000000000..b9f1c9502576f87ff98cc8fac612fd2b564d54fd --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastUpdated.php @@ -0,0 +1,31 @@ +ensureMyTable(); + $this->node_table = $this->query->ensure_table('node', $this->relationship); + $this->field_alias = $this->query->add_field(NULL, "GREATEST(" . $this->node_table . ".changed, " . $this->tableAlias . ".last_comment_timestamp)", $this->tableAlias . '_' . $this->field); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/NodeComment.php b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeComment.php new file mode 100644 index 0000000000000000000000000000000000000000..9902972400176f3cfed2a19788aa99d07511d6d0 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeComment.php @@ -0,0 +1,38 @@ +get_value($values); + switch ($value) { + case COMMENT_NODE_HIDDEN: + default: + return t('Hidden'); + case COMMENT_NODE_CLOSED: + return t('Closed'); + case COMMENT_NODE_OPEN: + return t('Open'); + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/NodeLink.php b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeLink.php new file mode 100644 index 0000000000000000000000000000000000000000..c59940305eee92e32595b18d67899916552abe9a --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeLink.php @@ -0,0 +1,57 @@ + FALSE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['teaser'] = array( + '#type' => 'checkbox', + '#title' => t('Show teaser-style link'), + '#default_value' => $this->options['teaser'], + '#description' => t('Show the comment link in the form used on standard node teasers, rather than the full node form.'), + ); + + parent::buildOptionsForm($form, $form_state); + } + + public function query() {} + + function render($values) { + $node = $this->get_entity($values); + + // Call comment.module's hook_link: comment_link($type, $node = NULL, $teaser = FALSE) + // Call node by reference so that something is changed here + comment_node_view($node, $this->options['teaser'] ? 'teaser' : 'full'); + // question: should we run these through: drupal_alter('link', $links, $node); + // might this have unexpected consequences if these hooks expect items in $node that we don't have? + + // Only render the links, if they are defined. + return !empty($node->content['links']['comment']) ? drupal_render($node->content['links']['comment']) : ''; + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/NodeNewComments.php b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeNewComments.php new file mode 100644 index 0000000000000000000000000000000000000000..fbc49793f843d9518da5d4bd199d656d68e5c456 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/NodeNewComments.php @@ -0,0 +1,124 @@ +additional_fields['nid'] = 'nid'; + $this->additional_fields['type'] = 'type'; + $this->additional_fields['comment_count'] = array('table' => 'node_comment_statistics', 'field' => 'comment_count'); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['link_to_comment'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_comment'] = array( + '#title' => t('Link this field to new comments'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_comment'], + ); + + parent::buildOptionsForm($form, $form_state); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + $this->field_alias = $this->table . '_' . $this->field; + } + + function pre_render(&$values) { + global $user; + if (!$user->uid || empty($values)) { + return; + } + + $nids = array(); + $ids = array(); + foreach ($values as $id => $result) { + $nids[] = $result->{$this->aliases['nid']}; + $values[$id]->{$this->field_alias} = 0; + // Create a reference so we can find this record in the values again. + if (empty($ids[$result->{$this->aliases['nid']}])) { + $ids[$result->{$this->aliases['nid']}] = array(); + } + $ids[$result->{$this->aliases['nid']}][] = $id; + } + + if ($nids) { + $query = db_select('node', 'n'); + $query->addField('n', 'nid'); + $query->innerJoin('comment', 'c', 'n.nid = c.nid'); + $query->addExpression('COUNT(c.cid)', 'num_comments'); + $query->leftJoin('history', 'h', 'h.nid = n.nid'); + $query->condition('n.nid', $nids); + $query->where('c.changed > GREATEST(COALESCE(h.timestamp, :timestamp), :timestamp)', array(':timestamp' => NODE_NEW_LIMIT)); + $query->condition('c.status', COMMENT_PUBLISHED); + $query->groupBy('n.nid'); + $result = $query->execute(); + foreach ($result as $node) { + foreach ($ids[$node->nid] as $id) { + $values[$id]->{$this->field_alias} = $node->num_comments; + } + } + } + } + + function render_link($data, $values) { + if (!empty($this->options['link_to_comment']) && $data !== NULL && $data !== '') { + $node = entity_create('node', array( + 'nid' => $this->get_value($values, 'nid'), + 'type' => $this->get_value($values, 'type'), + )); + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'node/' . $node->nid; + $this->options['alter']['query'] = comment_new_page_count($this->get_value($values, 'comment_count'), $this->get_value($values), $node); + $this->options['alter']['fragment'] = 'new'; + } + + return $data; + } + + function render($values) { + $value = $this->get_value($values); + if (!empty($value)) { + return $this->render_link(parent::render($values), $values); + } + else { + $this->options['alter']['make_link'] = FALSE; + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/field/Username.php b/core/modules/views/lib/Views/comment/Plugin/views/field/Username.php new file mode 100644 index 0000000000000000000000000000000000000000..52e8851a006f880b863366c1aab1aaff7a97797c --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/field/Username.php @@ -0,0 +1,71 @@ +additional_fields['uid'] = 'uid'; + $this->additional_fields['homepage'] = 'homepage'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_user'] = array('default' => TRUE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_user'] = array( + '#title' => t("Link this field to its user or an author's homepage"), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_user'], + ); + parent::buildOptionsForm($form, $form_state); + } + + function render_link($data, $values) { + if (!empty($this->options['link_to_user'])) { + $account = entity_create('user', array()); + $account->uid = $this->get_value($values, 'uid'); + $account->name = $this->get_value($values); + $account->homepage = $this->get_value($values, 'homepage'); + + return theme('username', array( + 'account' => $account + )); + } + else { + return $data; + } + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/filter/NcsLastUpdated.php b/core/modules/views/lib/Views/comment/Plugin/views/filter/NcsLastUpdated.php new file mode 100644 index 0000000000000000000000000000000000000000..2c4cd6b8408c77c087676aa4c661ac6e0c18926f --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/filter/NcsLastUpdated.php @@ -0,0 +1,37 @@ +ensureMyTable(); + $this->node_table = $this->query->ensure_table('node', $this->relationship); + + $field = "GREATEST(" . $this->node_table . ".changed, " . $this->tableAlias . ".last_comment_timestamp)"; + + $info = $this->operators(); + if (!empty($info[$this->operator]['method'])) { + $this->{$info[$this->operator]['method']}($field); + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/filter/NodeComment.php b/core/modules/views/lib/Views/comment/Plugin/views/filter/NodeComment.php new file mode 100644 index 0000000000000000000000000000000000000000..43ee9f9820bc7ffb9d334a59c4647799787c8aa1 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/filter/NodeComment.php @@ -0,0 +1,33 @@ +value_options = array( + COMMENT_NODE_HIDDEN => t('Hidden'), + COMMENT_NODE_CLOSED => t('Closed'), + COMMENT_NODE_OPEN => t('Open'), + ); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/filter/UserUid.php b/core/modules/views/lib/Views/comment/Plugin/views/filter/UserUid.php new file mode 100644 index 0000000000000000000000000000000000000000..1aa3083d3d6f394eacb9d02b4a3d20d8367b0a0f --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/filter/UserUid.php @@ -0,0 +1,41 @@ +ensureMyTable(); + + $subselect = db_select('comment', 'c'); + $subselect->addField('c', 'cid'); + $subselect->condition('c.uid', $this->value, $this->operator); + $subselect->where("c.nid = $this->tableAlias.nid"); + + $condition = db_or() + ->condition("$this->tableAlias.uid", $this->value, $this->operator) + ->exists($subselect); + + $this->query->add_where($this->options['group'], $condition); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/row/Rss.php b/core/modules/views/lib/Views/comment/Plugin/views/row/Rss.php new file mode 100644 index 0000000000000000000000000000000000000000..323df24c337dbd8b83eb2605a97bf6cd1522e1ff --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/row/Rss.php @@ -0,0 +1,168 @@ + 'default'); + $options['links'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['item_length'] = array( + '#type' => 'select', + '#title' => t('Display type'), + '#options' => $this->options_form_summary_options(), + '#default_value' => $this->options['item_length'], + ); + $form['links'] = array( + '#type' => 'checkbox', + '#title' => t('Display links'), + '#default_value' => $this->options['links'], + ); + } + + function pre_render($result) { + $cids = array(); + $nids = array(); + + foreach ($result as $row) { + $cids[] = $row->cid; + } + + $this->comments = comment_load_multiple($cids); + foreach ($this->comments as &$comment) { + $comment->depth = count(explode('.', $comment->thread)) - 1; + $nids[] = $comment->nid; + } + + $this->nodes = node_load_multiple($nids); + } + + /** + * Return the main options, which are shown in the summary title + * + * @see views_plugin_row_node_rss::options_form_summary_options() + * @todo: Maybe provide a views_plugin_row_rss_entity and reuse this method + * in views_plugin_row_comment|node_rss.inc + */ + function options_form_summary_options() { + $entity_info = entity_get_info('node'); + $options = array(); + if (!empty($entity_info['view modes'])) { + foreach ($entity_info['view modes'] as $mode => $settings) { + $options[$mode] = $settings['label']; + } + } + $options['title'] = t('Title only'); + $options['default'] = t('Use site default RSS settings'); + return $options; + } + + function render($row) { + global $base_url; + + $cid = $row->{$this->field_alias}; + if (!is_numeric($cid)) { + return; + } + + $item_length = $this->options['item_length']; + if ($item_length == 'default') { + $item_length = config('system.rss')->get('items.view_mode'); + } + + // Load the specified comment and its associated node: + $comment = $this->comments[$cid]; + if (empty($comment) || empty($this->nodes[$comment->nid])) { + return; + } + + $item_text = ''; + + $uri = $comment->uri(); + $comment->link = url($uri['path'], $uri['options'] + array('absolute' => TRUE)); + $comment->rss_namespaces = array(); + $comment->rss_elements = array( + array( + 'key' => 'pubDate', + 'value' => gmdate('r', $comment->created), + ), + array( + 'key' => 'dc:creator', + 'value' => $comment->name, + ), + array( + 'key' => 'guid', + 'value' => 'comment ' . $comment->id() . ' at ' . $base_url, + 'attributes' => array('isPermaLink' => 'false'), + ), + ); + + // The comment gets built and modules add to or modify + // $comment->rss_elements and $comment->rss_namespaces. + $build = comment_view($comment, $this->nodes[$comment->nid], 'rss'); + unset($build['#theme']); + + if (!empty($comment->rss_namespaces)) { + $this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $comment->rss_namespaces); + } + + // Hide the links if desired. + if (!$this->options['links']) { + hide($build['links']); + } + + if ($item_length != 'title') { + // We render comment contents and force links to be last. + $build['links']['#weight'] = 1000; + $item_text .= drupal_render($build); + } + + $item = new stdClass(); + $item->description = $item_text; + $item->title = $comment->label(); + $item->link = $comment->link; + $item->elements = $comment->rss_elements; + $item->cid = $comment->id(); + + return theme($this->themeFunctions(), array( + 'view' => $this->view, + 'options' => $this->options, + 'row' => $item + )); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/row/View.php b/core/modules/views/lib/Views/comment/Plugin/views/row/View.php new file mode 100644 index 0000000000000000000000000000000000000000..094971b5fc3e0ee5867a02588c3b1b450436cfff --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/row/View.php @@ -0,0 +1,65 @@ + TRUE); + $options['view_mode']['default'] = 'full'; + return $options; + } + + /** + * Overrides Views\system\Plugin\views\row\Entity::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['links'] = array( + '#type' => 'checkbox', + '#title' => t('Display links'), + '#default_value' => $this->options['links'], + ); + } + + /** + * Overrides Views\system\Plugin\views\row\Entity::render(). + */ + function render($row) { + $entity_id = $row->{$this->field_alias}; + $build = $this->build[$entity_id]; + if (!$this->options['links']) { + unset($build['links']); + } + return drupal_render($build); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastCommentName.php b/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastCommentName.php new file mode 100644 index 0000000000000000000000000000000000000000..8ba5d596316ec6f50e8dbc256b9f112eee43ae90 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastCommentName.php @@ -0,0 +1,47 @@ +ensureMyTable(); + $definition = array( + 'table' => 'users', + 'field' => 'uid', + 'left_table' => $this->tableAlias, + 'left_field' => 'last_comment_uid', + ); + $join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition); + + // @todo this might be safer if we had an ensure_relationship rather than guessing + // the table alias. Though if we did that we'd be guessing the relationship name + // so that doesn't matter that much. +// $this->user_table = $this->query->add_relationship(NULL, $join, 'users', $this->relationship); + $this->user_table = $this->query->ensure_table('ncs_users', $this->relationship, $join); + $this->user_field = $this->query->add_field($this->user_table, 'name'); + + // Add the field. + $this->query->add_orderby(NULL, "LOWER(COALESCE($this->user_table.name, $this->tableAlias.$this->field))", $this->options['order'], $this->tableAlias . '_' . $this->field); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastUpdated.php b/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastUpdated.php new file mode 100644 index 0000000000000000000000000000000000000000..ff60bd645bf0f6765d78a6580408f7cf1c3afb40 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastUpdated.php @@ -0,0 +1,31 @@ +ensureMyTable(); + $this->node_table = $this->query->ensure_table('node', $this->relationship); + $this->field_alias = $this->query->add_orderby(NULL, "GREATEST(" . $this->node_table . ".changed, " . $this->tableAlias . ".last_comment_timestamp)", $this->options['order'], $this->tableAlias . '_' . $this->field); + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/sort/Thread.php b/core/modules/views/lib/Views/comment/Plugin/views/sort/Thread.php new file mode 100644 index 0000000000000000000000000000000000000000..4377ea41e58bc5815d17863f70c0d9a98c6ab9f5 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/sort/Thread.php @@ -0,0 +1,40 @@ +ensureMyTable(); + + //Read comment_render() in comment.module for an explanation of the + //thinking behind this sort. + if ($this->options['order'] == 'DESC') { + $this->query->add_orderby($this->tableAlias, $this->realField, $this->options['order']); + } + else { + $alias = $this->tableAlias . '_' . $this->realField . 'asc'; + //@todo is this secure? + $this->query->add_orderby(NULL, "SUBSTRING({$this->tableAlias}.{$this->realField}, 1, (LENGTH({$this->tableAlias}.{$this->realField}) - 1))", $this->options['order'], $alias); + } + } + +} diff --git a/core/modules/views/lib/Views/comment/Plugin/views/wizard/Comment.php b/core/modules/views/lib/Views/comment/Plugin/views/wizard/Comment.php new file mode 100644 index 0000000000000000000000000000000000000000..dd67c520e329715b4d6bce41d6f596899d46d070 --- /dev/null +++ b/core/modules/views/lib/Views/comment/Plugin/views/wizard/Comment.php @@ -0,0 +1,174 @@ + 'cid', + 'table' => 'comment', + 'field' => 'cid', + 'exclude' => TRUE, + 'link_to_comment' => FALSE, + 'alter' => array( + 'alter_text' => TRUE, + 'text' => 'comment/[cid]#comment-[cid]' + ), + ); + + /** + * Set default values for the filters. + */ + protected $filters = array( + 'status' => array( + 'value' => TRUE, + 'table' => 'comment', + 'field' => 'status' + ), + 'status_node' => array( + 'value' => TRUE, + 'table' => 'node', + 'field' => 'status', + 'relationship' => 'nid' + ) + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::row_style_options(). + */ + protected function row_style_options() { + $options = array(); + $options['comment'] = t('comments'); + $options['fields'] = t('fields'); + return $options; + } + + protected function build_form_style(array &$form, array &$form_state, $type) { + parent::build_form_style($form, $form_state, $type); + $style_form =& $form['displays'][$type]['options']['style']; + // Some style plugins don't support row plugins so stop here if that's the + // case. + if (!isset($style_form['row_plugin']['#default_value'])) { + return; + } + $row_plugin = $style_form['row_plugin']['#default_value']; + switch ($row_plugin) { + case 'comment': + $style_form['row_options']['links'] = array( + '#type' => 'select', + '#title_display' => 'invisible', + '#title' => t('Should links be displayed below each comment'), + '#options' => array( + 1 => t('with links (allow users to reply to the comment, etc.)'), + 0 => t('without links'), + ), + '#default_value' => 1, + ); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::page_display_options(). + */ + protected function page_display_options(array $form, array &$form_state) { + $display_options = parent::page_display_options($form, $form_state); + $row_plugin = isset($form_state['values']['page']['style']['row_plugin']) ? $form_state['values']['page']['style']['row_plugin'] : NULL; + $row_options = isset($form_state['values']['page']['style']['row_options']) ? $form_state['values']['page']['style']['row_options'] : array(); + $this->display_options_row($display_options, $row_plugin, $row_options); + return $display_options; + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::page_display_options(). + */ + protected function block_display_options(array $form, array &$form_state) { + $display_options = parent::block_display_options($form, $form_state); + $row_plugin = isset($form_state['values']['block']['style']['row_plugin']) ? $form_state['values']['block']['style']['row_plugin'] : NULL; + $row_options = isset($form_state['values']['block']['style']['row_options']) ? $form_state['values']['block']['style']['row_options'] : array(); + $this->display_options_row($display_options, $row_plugin, $row_options); + return $display_options; + } + + /** + * Set the row style and row style plugins to the display_options. + */ + protected function display_options_row(&$display_options, $row_plugin, $row_options) { + switch ($row_plugin) { + case 'comment': + $display_options['row']['type'] = 'comment'; + $display_options['row']['options']['links'] = !empty($row_options['links']); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + + // Add a relationship to nodes. + $display_options['relationships']['nid']['id'] = 'nid'; + $display_options['relationships']['nid']['table'] = 'comment'; + $display_options['relationships']['nid']['field'] = 'nid'; + $display_options['relationships']['nid']['required'] = 1; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: Comment: Title */ + $display_options['fields']['subject']['id'] = 'subject'; + $display_options['fields']['subject']['table'] = 'comment'; + $display_options['fields']['subject']['field'] = 'subject'; + $display_options['fields']['subject']['label'] = ''; + $display_options['fields']['subject']['alter']['alter_text'] = 0; + $display_options['fields']['subject']['alter']['make_link'] = 0; + $display_options['fields']['subject']['alter']['absolute'] = 0; + $display_options['fields']['subject']['alter']['trim'] = 0; + $display_options['fields']['subject']['alter']['word_boundary'] = 0; + $display_options['fields']['subject']['alter']['ellipsis'] = 0; + $display_options['fields']['subject']['alter']['strip_tags'] = 0; + $display_options['fields']['subject']['alter']['html'] = 0; + $display_options['fields']['subject']['hide_empty'] = 0; + $display_options['fields']['subject']['empty_zero'] = 0; + $display_options['fields']['subject']['link_to_comment'] = 1; + + return $display_options; + } + +} diff --git a/core/modules/views/lib/Views/contact/Plugin/views/field/ContactLink.php b/core/modules/views/lib/Views/contact/Plugin/views/field/ContactLink.php new file mode 100644 index 0000000000000000000000000000000000000000..39b5c717e9e298745ef07758ae48872f64a76472 --- /dev/null +++ b/core/modules/views/lib/Views/contact/Plugin/views/field/ContactLink.php @@ -0,0 +1,68 @@ +options['text']) ? t('contact') : $this->options['text']; + parent::buildOptionsForm($form, $form_state); + } + + // An example of field level access control. + // We must override the access method in the parent class, as that requires + // the 'access user profiles' permission, which the contact form does not. + public function access() { + return user_access('access user contact forms'); + } + + function render_link($data, $values) { + global $user; + $uid = $this->get_value($values, 'uid'); + + if (empty($uid)) { + return; + } + + $account = user_load($uid); + if (empty($account)) { + return; + } + + // Check access when we pull up the user account so we know + // if the user has made the contact page available. + $menu_item = menu_get_item("user/$uid/contact"); + if (!$menu_item['access'] || empty($account->data['contact'])) { + return; + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'user/' . $account->uid . '/contact'; + $this->options['alter']['attributes'] = array('title' => t('Contact %user', array('%user' => $account->name))); + + $text = $this->options['text']; + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/field/Plugin/views/argument/FieldList.php b/core/modules/views/lib/Views/field/Plugin/views/argument/FieldList.php new file mode 100644 index 0000000000000000000000000000000000000000..f3b486b824c39075f91febb847287e63a45d5eef --- /dev/null +++ b/core/modules/views/lib/Views/field/Plugin/views/argument/FieldList.php @@ -0,0 +1,74 @@ +definition['field_name']); + $this->allowed_values = options_allowed_values($field); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['summary']['contains']['human'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['summary']['human'] = array( + '#title' => t('Display list value as human readable'), + '#type' => 'checkbox', + '#default_value' => $this->options['summary']['human'], + '#states' => array( + 'visible' => array( + ':input[name="options[default_action]"]' => array('value' => 'summary'), + ), + ), + ); + } + + function summary_name($data) { + $value = $data->{$this->name_alias}; + // If the list element has a human readable name show it, + if (isset($this->allowed_values[$value]) && !empty($this->options['summary']['human'])) { + return field_filter_xss($this->allowed_values[$value]); + } + // else fallback to the key. + else { + return check_plain($value); + } + } + +} diff --git a/core/modules/views/lib/Views/field/Plugin/views/argument/ListString.php b/core/modules/views/lib/Views/field/Plugin/views/argument/ListString.php new file mode 100644 index 0000000000000000000000000000000000000000..6ac3f8d5c579fa7f4391306d96315931ef0459bb --- /dev/null +++ b/core/modules/views/lib/Views/field/Plugin/views/argument/ListString.php @@ -0,0 +1,76 @@ +definition['field_name']); + $this->allowed_values = options_allowed_values($field); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['summary']['contains']['human'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['summary']['human'] = array( + '#title' => t('Display list value as human readable'), + '#type' => 'checkbox', + '#default_value' => $this->options['summary']['human'], + '#states' => array( + 'visible' => array( + ':input[name="options[default_action]"]' => array('value' => 'summary'), + ), + ), + ); + } + + + function summary_name($data) { + $value = $data->{$this->name_alias}; + // If the list element has a human readable name show it, + if (isset($this->allowed_values[$value]) && !empty($this->options['summary']['human'])) { + return $this->caseTransform(field_filter_xss($this->allowed_values[$value]), $this->options['case']); + } + // else fallback to the key. + else { + return $this->caseTransform(check_plain($value), $this->options['case']); + } + } + +} diff --git a/core/modules/views/lib/Views/field/Plugin/views/field/Field.php b/core/modules/views/lib/Views/field/Plugin/views/field/Field.php new file mode 100644 index 0000000000000000000000000000000000000000..670c0df7b6ba1aa6f1ba3669f8077edb4e732128 --- /dev/null +++ b/core/modules/views/lib/Views/field/Plugin/views/field/Field.php @@ -0,0 +1,854 @@ +base_table. + * + * @var string + */ + public $base_table; + + /** + * Store the field instance. + * + * @var array + */ + public $instance; + + public function init(ViewExecutable $view, &$options) { + parent::init($view, $options); + + $this->field_info = $field = field_info_field($this->definition['field_name']); + $this->multiple = FALSE; + $this->limit_values = FALSE; + + if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) { + $this->multiple = TRUE; + + // If "Display all values in the same row" is FALSE, then we always limit + // in order to show a single unique value per row. + if (!$this->options['group_rows']) { + $this->limit_values = TRUE; + } + + // If "First and last only" is chosen, limit the values + if (!empty($this->options['delta_first_last'])) { + $this->limit_values = TRUE; + } + + // Otherwise, we only limit values if the user hasn't selected "all", 0, or + // the value matching field cardinality. + if ((intval($this->options['delta_limit']) && ($this->options['delta_limit'] != $field['cardinality'])) || intval($this->options['delta_offset'])) { + $this->limit_values = TRUE; + } + } + } + + /** + * Check whether current user has access to this handler. + * + * @return bool + * Return TRUE if the user has access to view this field. + */ + public function access() { + $base_table = $this->get_base_table(); + return field_access('view', $this->field_info, $this->definition['entity_tables'][$base_table]); + } + + /** + * Set the base_table and base_table_alias. + * + * @return string + * The base table which is used in the current view "context". + */ + function get_base_table() { + if (!isset($this->base_table)) { + // This base_table is coming from the entity not the field. + $this->base_table = $this->view->storage->base_table; + + // If the current field is under a relationship you can't be sure that the + // base table of the view is the base table of the current field. + // For example a field from a node author on a node view does have users as base table. + if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') { + $relationships = $this->view->display_handler->getOption('relationships'); + if (!empty($relationships[$this->options['relationship']])) { + $options = $relationships[$this->options['relationship']]; + $data = views_fetch_data($options['table']); + $this->base_table = $data[$options['field']]['relationship']['base']; + } + } + } + + return $this->base_table; + } + + /** + * Called to add the field to a query. + * + * By default, all needed data is taken from entities loaded by the query + * plugin. Columns are added only if they are used in groupings. + */ + public function query($use_groupby = FALSE) { + $this->get_base_table(); + + $entity_type = $this->definition['entity_tables'][$this->base_table]; + $fields = $this->additional_fields; + // No need to add the entity type. + $entity_type_key = array_search('entity_type', $fields); + if ($entity_type_key !== FALSE) { + unset($fields[$entity_type_key]); + } + + if ($use_groupby) { + // Add the fields that we're actually grouping on. + $options = array(); + if ($this->options['group_column'] != 'entity_id') { + $options = array($this->options['group_column'] => $this->options['group_column']); + } + $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : array(); + + $fields = array(); + $rkey = $this->definition['is revision'] ? 'FIELD_LOAD_REVISION' : 'FIELD_LOAD_CURRENT'; + // Go through the list and determine the actual column name from field api. + foreach ($options as $column) { + $name = $column; + if (isset($this->field_info['storage']['details']['sql'][$rkey][$this->table][$column])) { + $name = $this->field_info['storage']['details']['sql'][$rkey][$this->table][$column]; + } + + $fields[$column] = $name; + } + + $this->group_fields = $fields; + } + + // Add additional fields (and the table join itself) if needed. + if ($this->add_field_table($use_groupby)) { + $this->ensureMyTable(); + $this->add_additional_fields($fields); + + // Filter by langcode, if field translation is enabled. + $field = $this->field_info; + if (field_is_translatable($entity_type, $field) && !empty($this->view->display_handler->options['field_langcode_add_to_query'])) { + $column = $this->tableAlias . '.langcode'; + // By the same reason as field_language the field might be LANGUAGE_NOT_SPECIFIED in reality so allow it as well. + // @see this::field_langcode() + $default_langcode = language_default()->langcode; + $langcode = str_replace(array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'), + array(drupal_container()->get(LANGUAGE_TYPE_CONTENT)->langcode, $default_langcode), + $this->view->display_handler->options['field_langcode']); + $placeholder = $this->placeholder(); + $langcode_fallback_candidates = array($langcode); + if (variable_get('locale_field_language_fallback', TRUE)) { + require_once DRUPAL_ROOT . '/includes/language.inc'; + $langcode_fallback_candidates = array_merge($langcode_fallback_candidates, language_fallback_get_candidates()); + } + else { + $langcode_fallback_candidates[] = LANGUAGE_NOT_SPECIFIED; + } + $this->query->add_where_expression(0, "$column IN($placeholder) OR $column IS NULL", array($placeholder => $langcode_fallback_candidates)); + } + } + } + + /** + * Determine if the field table should be added to the query. + */ + function add_field_table($use_groupby) { + // Grouping is enabled. + if ($use_groupby) { + return TRUE; + } + // This a multiple value field, but "group multiple values" is not checked. + if ($this->multiple && !$this->options['group_rows']) { + return TRUE; + } + return FALSE; + } + + /** + * Determine if this field is click sortable. + */ + function click_sortable() { + // Not click sortable in any case. + if (empty($this->definition['click sortable'])) { + return FALSE; + } + // A field is not click sortable if it's a multiple field with + // "group multiple values" checked, since a click sort in that case would + // add a join to the field table, which would produce unwanted duplicates. + if ($this->multiple && $this->options['group_rows']) { + return FALSE; + } + return TRUE; + } + + /** + * Called to determine what to tell the clicksorter. + */ + function click_sort($order) { + // No column selected, can't continue. + if (empty($this->options['click_sort_column'])) { + return; + } + + $this->ensureMyTable(); + $column = _field_sql_storage_columnname($this->definition['field_name'], $this->options['click_sort_column']); + if (!isset($this->aliases[$column])) { + // Column is not in query; add a sort on it (without adding the column). + $this->aliases[$column] = $this->tableAlias . '.' . $column; + } + $this->query->add_orderby(NULL, NULL, $order, $this->aliases[$column]); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + // defineOptions runs before init/construct, so no $this->field_info + $field = field_info_field($this->definition['field_name']); + $field_type = field_info_field_types($field['type']); + $column_names = array_keys($field['columns']); + $default_column = ''; + // Try to determine a sensible default. + if (count($column_names) == 1) { + $default_column = $column_names[0]; + } + elseif (in_array('value', $column_names)) { + $default_column = 'value'; + } + + // If the field has a "value" column, we probably need that one. + $options['click_sort_column'] = array( + 'default' => $default_column, + ); + $options['type'] = array( + 'default' => $field_type['default_formatter'], + ); + $options['settings'] = array( + 'default' => array(), + ); + $options['group_column'] = array( + 'default' => $default_column, + ); + $options['group_columns'] = array( + 'default' => array(), + ); + + // Options used for multiple value fields. + $options['group_rows'] = array( + 'default' => TRUE, + 'bool' => TRUE, + ); + // If we know the exact number of allowed values, then that can be + // the default. Otherwise, default to 'all'. + $options['delta_limit'] = array( + 'default' => ($field['cardinality'] > 1) ? $field['cardinality'] : 'all', + ); + $options['delta_offset'] = array( + 'default' => 0, + ); + $options['delta_reversed'] = array( + 'default' => FALSE, + 'bool' => TRUE, + ); + $options['delta_first_last'] = array( + 'default' => FALSE, + 'bool' => TRUE, + ); + + $options['multi_type'] = array( + 'default' => 'separator' + ); + $options['separator'] = array( + 'default' => ', ' + ); + + $options['field_api_classes'] = array( + 'default' => FALSE, + 'bool' => TRUE, + ); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $field = $this->field_info; + $formatters = _field_view_formatter_options($field['type']); + $column_names = array_keys($field['columns']); + + // If this is a multiple value field, add its options. + if ($this->multiple) { + $this->multiple_options_form($form, $form_state); + } + + // No need to ask the user anything if the field has only one column. + if (count($field['columns']) == 1) { + $form['click_sort_column'] = array( + '#type' => 'value', + '#value' => isset($column_names[0]) ? $column_names[0] : '', + ); + } + else { + $form['click_sort_column'] = array( + '#type' => 'select', + '#title' => t('Column used for click sorting'), + '#options' => drupal_map_assoc($column_names), + '#default_value' => $this->options['click_sort_column'], + '#description' => t('Used by Style: Table to determine the actual column to click sort the field on. The default is usually fine.'), + '#fieldset' => 'more', + ); + } + + $form['type'] = array( + '#type' => 'select', + '#title' => t('Formatter'), + '#options' => $formatters, + '#default_value' => $this->options['type'], + '#ajax' => array( + 'path' => views_ui_build_form_url($form_state), + ), + '#submit' => array('views_ui_config_item_form_submit_temporary'), + '#executes_submit_callback' => TRUE, + ); + + $form['field_api_classes'] = array( + '#title' => t('Use field template'), + '#type' => 'checkbox', + '#default_value' => $this->options['field_api_classes'], + '#description' => t('If checked, field api classes will be added using field.tpl.php (or equivalent). This is not recommended unless your CSS depends upon these classes. If not checked, template will not be used.'), + '#fieldset' => 'style_settings', + '#weight' => 20, + ); + + if ($this->multiple) { + $form['field_api_classes']['#description'] .= ' ' . t('Checking this option will cause the group Display Type and Separator values to be ignored.'); + } + + // Get the currently selected formatter. + $format = $this->options['type']; + + $formatter = field_info_formatter_types($format); + $settings = $this->options['settings'] + field_info_formatter_settings($format); + + // Provide an instance array for hook_field_formatter_settings_form(). + $this->instance = $this->fakeFieldInstance($format, $settings); + + // Get the settings form. + $settings_form = array('#value' => array()); + $function = $formatter['module'] . '_field_formatter_settings_form'; + if (function_exists($function)) { + $settings_form = $function($field, $this->instance, '_custom', $form, $form_state); + } + $form['settings'] = $settings_form; + } + + /** + * Provides a fake field instance. + * + * @param string $formatter + * The machine name of the formatter to use. + * @param array $formatter_settings + * An associative array of settings for the formatter. + * + * @return array + * An associative array of instance date for the fake field. + * + * @see field_info_instance() + */ + function fakeFieldInstance($formatter, $formatter_settings) { + $field_name = $this->definition['field_name']; + $field = field_read_field($field_name); + + $field_type = field_info_field_types($field['type']); + + return array( + // Build a fake entity type and bundle. + 'field_name' => $field_name, + 'entity_type' => 'views_fake', + 'bundle' => 'views_fake', + + // Use the default field settings for settings and widget. + 'settings' => field_info_instance_settings($field['type']), + 'widget' => array( + 'type' => $field_type['default_widget'], + 'settings' => array(), + ), + + // Build a dummy display mode. + 'display' => array( + '_custom' => array( + 'type' => $formatter, + 'settings' => $formatter_settings, + ), + ), + + // Set the other fields to their default values. + // @see _field_write_instance(). + 'required' => FALSE, + 'label' => $field_name, + 'description' => '', + 'deleted' => 0, + ); + } + + /** + * Provide options for multiple value fields. + */ + function multiple_options_form(&$form, &$form_state) { + $field = $this->field_info; + + $form['multiple_field_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Multiple field settings'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => 5, + ); + + $form['group_rows'] = array( + '#title' => t('Display all values in the same row'), + '#type' => 'checkbox', + '#default_value' => $this->options['group_rows'], + '#description' => t('If checked, multiple values for this field will be shown in the same row. If not checked, each value in this field will create a new row. If using group by, please make sure to group by "Entity ID" for this setting to have any effect.'), + '#fieldset' => 'multiple_field_settings', + ); + + // Make the string translatable by keeping it as a whole rather than + // translating prefix and suffix separately. + list($prefix, $suffix) = explode('@count', t('Display @count value(s)')); + + if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) { + $type = 'textfield'; + $options = NULL; + $size = 5; + } + else { + $type = 'select'; + $options = drupal_map_assoc(range(1, $field['cardinality'])); + $size = 1; + } + $form['multi_type'] = array( + '#type' => 'radios', + '#title' => t('Display type'), + '#options' => array( + 'ul' => t('Unordered list'), + 'ol' => t('Ordered list'), + 'separator' => t('Simple separator'), + ), + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ), + ), + '#default_value' => $this->options['multi_type'], + '#fieldset' => 'multiple_field_settings', + ); + + $form['separator'] = array( + '#type' => 'textfield', + '#title' => t('Separator'), + '#default_value' => $this->options['separator'], + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ':input[name="options[multi_type]"]' => array('value' => 'separator'), + ), + ), + '#fieldset' => 'multiple_field_settings', + ); + + $form['delta_limit'] = array( + '#type' => $type, + '#size' => $size, + '#field_prefix' => $prefix, + '#field_suffix' => $suffix, + '#options' => $options, + '#default_value' => $this->options['delta_limit'], + '#prefix' => '', + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ), + ), + '#fieldset' => 'multiple_field_settings', + ); + + list($prefix, $suffix) = explode('@count', t('starting from @count')); + $form['delta_offset'] = array( + '#type' => 'textfield', + '#size' => 5, + '#field_prefix' => $prefix, + '#field_suffix' => $suffix, + '#default_value' => $this->options['delta_offset'], + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ), + ), + '#description' => t('(first item is 0)'), + '#fieldset' => 'multiple_field_settings', + ); + $form['delta_reversed'] = array( + '#title' => t('Reversed'), + '#type' => 'checkbox', + '#default_value' => $this->options['delta_reversed'], + '#suffix' => $suffix, + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ), + ), + '#description' => t('(start from last values)'), + '#fieldset' => 'multiple_field_settings', + ); + $form['delta_first_last'] = array( + '#title' => t('First and last only'), + '#type' => 'checkbox', + '#default_value' => $this->options['delta_first_last'], + '#suffix' => '', + '#states' => array( + 'visible' => array( + ':input[name="options[group_rows]"]' => array('checked' => TRUE), + ), + ), + '#fieldset' => 'multiple_field_settings', + ); + } + + /** + * Extend the groupby form with group columns. + */ + public function buildGroupByForm(&$form, &$form_state) { + parent::buildGroupByForm($form, $form_state); + // With "field API" fields, the column target of the grouping function + // and any additional grouping columns must be specified. + $group_columns = array( + 'entity_id' => t('Entity ID'), + ) + drupal_map_assoc(array_keys($this->field_info['columns']), 'ucfirst'); + + $form['group_column'] = array( + '#type' => 'select', + '#title' => t('Group column'), + '#default_value' => $this->options['group_column'], + '#description' => t('Select the column of this field to apply the grouping function selected above.'), + '#options' => $group_columns, + ); + + $options = drupal_map_assoc(array('bundle', 'language', 'entity_type'), 'ucfirst'); + + // Add on defined fields, noting that they're prefixed with the field name. + $form['group_columns'] = array( + '#type' => 'checkboxes', + '#title' => t('Group columns (additional)'), + '#default_value' => $this->options['group_columns'], + '#description' => t('Select any additional columns of this field to include in the query and to group on.'), + '#options' => $options + $group_columns, + ); + } + + public function submitGroupByForm(&$form, &$form_state) { + parent::submitGroupByForm($form, $form_state); + $item =& $form_state['handler']->options; + + // Add settings for "field API" fields. + $item['group_column'] = $form_state['values']['options']['group_column']; + $item['group_columns'] = array_filter($form_state['values']['options']['group_columns']); + } + + /** + * Render all items in this field together. + * + * When using advanced render, each possible item in the list is rendered + * individually. Then the items are all pasted together. + */ + function render_items($items) { + if (!empty($items)) { + if (!$this->options['group_rows']) { + return implode('', $items); + } + + if ($this->options['multi_type'] == 'separator') { + return implode(filter_xss_admin($this->options['separator']), $items); + } + else { + return theme('item_list', + array( + 'items' => $items, + 'title' => NULL, + 'type' => $this->options['multi_type'] + )); + } + } + } + + /** + * Return an array of items for the field. + */ + function get_items($values) { + $original_entity = $this->get_entity($values); + if (!$original_entity) { + return array(); + } + $entity = $this->process_entity($original_entity); + if (!$entity) { + return array(); + } + + $display = array( + 'type' => $this->options['type'], + 'settings' => $this->options['settings'], + 'label' => 'hidden', + // Pass the View object in the display so that fields can act on it. + 'views_view' => $this->view, + 'views_field' => $this, + 'views_row_id' => $this->view->row_index, + ); + + $entity_type = $entity->entityType(); + $langcode = $this->field_langcode($entity_type, $entity); + $render_array = field_view_field($entity_type, $entity, $this->definition['field_name'], $display, $langcode); + + $items = array(); + if ($this->options['field_api_classes']) { + // Make a copy. + $array = $render_array; + return array(array('rendered' => drupal_render($render_array))); + } + + foreach (element_children($render_array) as $count) { + $items[$count]['rendered'] = $render_array[$count]; + // field_view_field() adds an #access property to the render array that + // determines whether or not the current user is allowed to view the + // field in the context of the current entity. We need to respect this + // parameter when we pull out the children of the field array for + // rendering. + if (isset($render_array['#access'])) { + $items[$count]['rendered']['#access'] = $render_array['#access']; + } + // Only add the raw field items (for use in tokens) if the current user + // has access to view the field content. + if ((!isset($items[$count]['rendered']['#access']) || $items[$count]['rendered']['#access']) && !empty($render_array['#items'][$count])) { + $items[$count]['raw'] = $render_array['#items'][$count]; + } + } + return $items; + } + + /** + * Process an entity before using it for rendering. + * + * Replaces values with aggregated values if aggregation is enabled. + * Takes delta settings into account (@todo remove in #1758616). + * + * @param $entity + * The entity to be processed. + * + * @return + * TRUE if the processing completed successfully, otherwise FALSE. + */ + function process_entity($entity) { + $processed_entity = clone $entity; + + $entity_type = $entity->entityType(); + $langcode = $this->field_langcode($entity_type, $processed_entity); + // If we are grouping, copy our group fields into the cloned entity. + // It's possible this will cause some weirdness, but there's only + // so much we can hope to do. + if (!empty($this->group_fields)) { + // first, test to see if we have a base value. + $base_value = array(); + // Note: We would copy original values here, but it can cause problems. + // For example, text fields store cached filtered values as + // 'safe_value' which doesn't appear anywhere in the field definition + // so we can't affect it. Other side effects could happen similarly. + $data = FALSE; + foreach ($this->group_fields as $field_name => $column) { + if (property_exists($values, $this->aliases[$column])) { + $base_value[$field_name] = $values->{$this->aliases[$column]}; + if (isset($base_value[$field_name])) { + $data = TRUE; + } + } + } + + // If any of our aggregated fields have data, fake it: + if ($data) { + // Now, overwrite the original value with our aggregated value. + // This overwrites it so there is always just one entry. + $processed_entity->{$this->definition['field_name']}[$langcode] = array($base_value); + } + else { + $processed_entity->{$this->definition['field_name']}[$langcode] = array(); + } + } + + // The field we are trying to display doesn't exist on this entity. + if (!isset($processed_entity->{$this->definition['field_name']})) { + return FALSE; + } + + // We are supposed to show only certain deltas. + if ($this->limit_values && !empty($processed_entity->{$this->definition['field_name']})) { + $all_values = !empty($processed_entity->{$this->definition['field_name']}[$langcode]) ? $processed_entity->{$this->definition['field_name']}[$langcode] : array(); + if ($this->options['delta_reversed']) { + $all_values = array_reverse($all_values); + } + + // Offset is calculated differently when row grouping for a field is + // not enabled. Since there are multiple rows, the delta needs to be + // taken into account, so that different values are shown per row. + if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($values->{$this->aliases['delta']})) { + $delta_limit = 1; + $offset = $values->{$this->aliases['delta']}; + } + // Single fields don't have a delta available so choose 0. + elseif (!$this->options['group_rows'] && !$this->multiple) { + $delta_limit = 1; + $offset = 0; + } + else { + $delta_limit = $this->options['delta_limit']; + $offset = intval($this->options['delta_offset']); + + // We should only get here in this case if there's an offset, and + // in that case we're limiting to all values after the offset. + if ($delta_limit == 'all') { + $delta_limit = count($all_values) - $offset; + } + } + + // Determine if only the first and last values should be shown + $delta_first_last = $this->options['delta_first_last']; + + $new_values = array(); + for ($i = 0; $i < $delta_limit; $i++) { + $new_delta = $offset + $i; + + if (isset($all_values[$new_delta])) { + // If first-last option was selected, only use the first and last values + if (!$delta_first_last + // Use the first value. + || $new_delta == $offset + // Use the last value. + || $new_delta == ($delta_limit + $offset - 1)) { + $new_values[] = $all_values[$new_delta]; + } + } + } + $processed_entity->{$this->definition['field_name']}[$langcode] = $new_values; + } + + return $processed_entity; + } + + function render_item($count, $item) { + return render($item['rendered']); + } + + function document_self_tokens(&$tokens) { + $field = $this->field_info; + foreach ($field['columns'] as $id => $column) { + $tokens['[' . $this->options['id'] . '-' . $id . ']'] = t('Raw @column', array('@column' => $id)); + } + } + + function add_self_tokens(&$tokens, $item) { + $field = $this->field_info; + foreach ($field['columns'] as $id => $column) { + // Use filter_xss_admin because it's user data and we can't be sure it is safe. + // We know nothing about the data, though, so we can't really do much else. + + if (isset($item['raw'])) { + // If $item['raw'] is an array then we can use as is, if it's an object + // we cast it to an array, if it's neither, we can't use it. + $raw = is_array($item['raw']) ? $item['raw'] : + (is_object($item['raw']) ? (array)$item['raw'] : NULL); + } + if (isset($raw) && isset($raw[$id]) && is_scalar($raw[$id])) { + $tokens['[' . $this->options['id'] . '-' . $id . ']'] = filter_xss_admin($raw[$id]); + } + else { + // Take sure that empty values are replaced as well. + $tokens['[' . $this->options['id'] . '-' . $id . ']'] = ''; + } + } + } + + /** + * Return the language code of the language the field should be displayed in, + * according to the settings. + */ + function field_langcode($entity_type, $entity) { + if (field_is_translatable($entity_type, $this->field_info)) { + $default_langcode = language_default()->langcode; + $langcode = str_replace(array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'), + array(drupal_container()->get(LANGUAGE_TYPE_CONTENT)->langcode, $default_langcode), + $this->view->display_handler->options['field_language']); + + // Give the Field Language API a chance to fallback to a different language + // (or LANGUAGE_NOT_SPECIFIED), in case the field has no data for the selected language. + // field_view_field() does this as well, but since the returned language code + // is used before calling it, the fallback needs to happen explicitly. + $langcode = field_language($entity_type, $entity, $this->field_info['field_name'], $langcode); + + return $langcode; + } + else { + return LANGUAGE_NOT_SPECIFIED; + } + } + +} diff --git a/core/modules/views/lib/Views/field/Plugin/views/filter/FieldList.php b/core/modules/views/lib/Views/field/Plugin/views/filter/FieldList.php new file mode 100644 index 0000000000000000000000000000000000000000..d5acfcdf0d00f98abe7d3ab4f994274bb4aa81a1 --- /dev/null +++ b/core/modules/views/lib/Views/field/Plugin/views/filter/FieldList.php @@ -0,0 +1,30 @@ +definition['field_name']); + $this->value_options = list_allowed_values($field); + } + +} diff --git a/core/modules/views/lib/Views/field/Plugin/views/relationship/EntityReverse.php b/core/modules/views/lib/Views/field/Plugin/views/relationship/EntityReverse.php new file mode 100644 index 0000000000000000000000000000000000000000..e98a74233385e37dd1a4a5fff83a43974ce0c809 --- /dev/null +++ b/core/modules/views/lib/Views/field/Plugin/views/relationship/EntityReverse.php @@ -0,0 +1,97 @@ +field_info = field_info_field($this->definition['field_name']); + } + + /** + * Called to implement a relationship in a query. + */ + public function query() { + $this->ensureMyTable(); + // First, relate our base table to the current base table to the + // field, using the base table's id field to the field's column. + $views_data = views_fetch_data($this->table); + $left_field = $views_data['table']['base']['field']; + + $first = array( + 'left_table' => $this->tableAlias, + 'left_field' => $left_field, + 'table' => $this->definition['field table'], + 'field' => $this->definition['field field'], + 'adjusted' => TRUE + ); + if (!empty($this->options['required'])) { + $first['type'] = 'INNER'; + } + + if (!empty($this->definition['join_extra'])) { + $first['extra'] = $this->definition['join_extra']; + } + + if (!empty($def['join_id'])) { + $id = $def['join_id']; + } + else { + $id = 'standard'; + } + $first_join = drupal_container()->get('plugin.manager.views.join')->createInstance($id, $first); + + + $this->first_alias = $this->query->add_table($this->definition['field table'], $this->relationship, $first_join); + + // Second, relate the field table to the entity specified using + // the entity id on the field table and the entity's id field. + $second = array( + 'left_table' => $this->first_alias, + 'left_field' => 'entity_id', + 'table' => $this->definition['base'], + 'field' => $this->definition['base field'], + 'adjusted' => TRUE + ); + + if (!empty($this->options['required'])) { + $second['type'] = 'INNER'; + } + + if (!empty($def['join_id'])) { + $id = $def['join_id']; + } + else { + $id = 'standard'; + } + $second_join = drupal_container()->get('plugin.manager.views.join')->createInstance($id, $second); + $second_join->adjusted = TRUE; + + // use a short alias for this: + $alias = $this->definition['field_name'] . '_' . $this->table; + + $this->alias = $this->query->add_relationship($alias, $second_join, $this->definition['base'], $this->relationship); + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/argument/Fid.php b/core/modules/views/lib/Views/file/Plugin/views/argument/Fid.php new file mode 100644 index 0000000000000000000000000000000000000000..cf9c9d213cabcedab887036702b5fdf8ed2d2eea --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/argument/Fid.php @@ -0,0 +1,40 @@ +fields('f', array('filename')) + ->condition('fid', $this->value) + ->execute() + ->fetchCol(); + foreach ($titles as &$title) { + $title = check_plain($title); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/field/Extension.php b/core/modules/views/lib/Views/file/Plugin/views/field/Extension.php new file mode 100644 index 0000000000000000000000000000000000000000..792bb137214e73f56a6145c1bb64c93294b6e822 --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/field/Extension.php @@ -0,0 +1,32 @@ +get_value($values); + if (preg_match('/\.([^\.]+)$/', $value, $match)) { + return $this->sanitizeValue($match[1]); + } + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/field/File.php b/core/modules/views/lib/Views/file/Plugin/views/field/File.php new file mode 100644 index 0000000000000000000000000000000000000000..a10f88db9d8b4c00efdb9d54bd88bef992365312 --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/field/File.php @@ -0,0 +1,74 @@ +additional_fields['uri'] = 'uri'; + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_file'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to file option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_file'] = array( + '#title' => t('Link this field to download the file'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_file']), + ); + parent::buildOptionsForm($form, $form_state); + } + + /** + * Render whatever the data is as a link to the file. + * + * Data should be made XSS safe prior to calling this function. + */ + function render_link($data, $values) { + if (!empty($this->options['link_to_file']) && $data !== NULL && $data !== '') { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = file_create_url($this->get_value($values, 'uri')); + } + + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/field/FileMime.php b/core/modules/views/lib/Views/file/Plugin/views/field/FileMime.php new file mode 100644 index 0000000000000000000000000000000000000000..ebef8cb75e386d81c0040b7a103a0d0abe4b95fb --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/field/FileMime.php @@ -0,0 +1,49 @@ + FALSE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['filemime_image'] = array( + '#title' => t('Display an icon representing the file type, instead of the MIME text (such as "image/jpeg")'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['filemime_image']), + ); + parent::buildOptionsForm($form, $form_state); + } + + function render($values) { + $data = $values->{$this->field_alias}; + if (!empty($this->options['filemime_image']) && $data !== NULL && $data !== '') { + $fake_file = (object) array('filemime' => $data); + $data = theme('file_icon', array('file' => $fake_file)); + } + + return $this->render_link($data, $values); + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/field/Status.php b/core/modules/views/lib/Views/file/Plugin/views/field/Status.php new file mode 100644 index 0000000000000000000000000000000000000000..a90e07131c9a7cd11e3ed121cf3a92e4b610d4d2 --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/field/Status.php @@ -0,0 +1,30 @@ +get_value($values); + return _views_file_status($value); + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/field/Uri.php b/core/modules/views/lib/Views/file/Plugin/views/field/Uri.php new file mode 100644 index 0000000000000000000000000000000000000000..9a804aa4b5d72cb2b970851135aec52227678e2c --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/field/Uri.php @@ -0,0 +1,46 @@ + FALSE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['file_download_path'] = array( + '#title' => t('Display download path instead of file storage URI'), + '#description' => t('This will provide the full download URL rather than the internal filestream address.'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['file_download_path']), + ); + parent::buildOptionsForm($form, $form_state); + } + + function render($values) { + $data = $values->{$this->field_alias}; + if (!empty($this->options['file_download_path']) && $data !== NULL && $data !== '') { + $data = file_create_url($data); + } + return $this->render_link($data, $values); + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/filter/Status.php b/core/modules/views/lib/Views/file/Plugin/views/filter/Status.php new file mode 100644 index 0000000000000000000000000000000000000000..914e9db509aa38b19438b2d6d86c3e2b24951a19 --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/filter/Status.php @@ -0,0 +1,31 @@ +value_options)) { + $this->value_options = _views_file_status(); + } + } + +} diff --git a/core/modules/views/lib/Views/file/Plugin/views/wizard/File.php b/core/modules/views/lib/Views/file/Plugin/views/wizard/File.php new file mode 100644 index 0000000000000000000000000000000000000000..0408617610e3ff5699a95c8a536960de51a1d81b --- /dev/null +++ b/core/modules/views/lib/Views/file/Plugin/views/wizard/File.php @@ -0,0 +1,74 @@ + 'uri', + 'table' => 'file_managed', + 'field' => 'uri', + 'exclude' => TRUE, + 'file_download_path' => TRUE + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: File: Name */ + $display_options['fields']['filename']['id'] = 'filename'; + $display_options['fields']['filename']['table'] = 'file_managed'; + $display_options['fields']['filename']['field'] = 'filename'; + $display_options['fields']['filename']['label'] = ''; + $display_options['fields']['filename']['alter']['alter_text'] = 0; + $display_options['fields']['filename']['alter']['make_link'] = 0; + $display_options['fields']['filename']['alter']['absolute'] = 0; + $display_options['fields']['filename']['alter']['trim'] = 0; + $display_options['fields']['filename']['alter']['word_boundary'] = 0; + $display_options['fields']['filename']['alter']['ellipsis'] = 0; + $display_options['fields']['filename']['alter']['strip_tags'] = 0; + $display_options['fields']['filename']['alter']['html'] = 0; + $display_options['fields']['filename']['hide_empty'] = 0; + $display_options['fields']['filename']['empty_zero'] = 0; + $display_options['fields']['filename']['link_to_file'] = 1; + + return $display_options; + } + +} diff --git a/core/modules/views/lib/Views/filter/Plugin/views/field/FormatName.php b/core/modules/views/lib/Views/filter/Plugin/views/field/FormatName.php new file mode 100644 index 0000000000000000000000000000000000000000..81fd0bd2ed24f9f1439618e5ae728f709223cd4c --- /dev/null +++ b/core/modules/views/lib/Views/filter/Plugin/views/field/FormatName.php @@ -0,0 +1,53 @@ +additional_fields['name'] = array('table' => 'filter_formats', 'field' => 'name'); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + function render($values) { + $format_name = $this->get_value($values, 'name'); + if (!$format_name) { + // Default or invalid input format. + // filter_formats() will reliably return the default format even if the + // current user is unprivileged. + $format = filter_formats(filter_default_format()); + return $this->sanitizeValue($format->name); + } + return $this->sanitizeValue($format_name); + } + +} diff --git a/core/modules/views/lib/Views/language/Plugin/views/argument/Language.php b/core/modules/views/lib/Views/language/Plugin/views/argument/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..f53041d10196db32fc38f01e6ffca19f0b34adc3 --- /dev/null +++ b/core/modules/views/lib/Views/language/Plugin/views/argument/Language.php @@ -0,0 +1,46 @@ +language($data->{$this->name_alias}); + } + + /** + * Override the behavior of title(). Get the user friendly version + * of the language. + */ + function title() { + return $this->language($this->argument); + } + + function language($langcode) { + $languages = views_language_list(); + return isset($languages[$langcode]) ? $languages[$langcode] : t('Unknown language'); + } + +} diff --git a/core/modules/views/lib/Views/language/Plugin/views/field/Language.php b/core/modules/views/lib/Views/language/Plugin/views/field/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..104ae3f34e24d50a6072332b25da8a727ad37861 --- /dev/null +++ b/core/modules/views/lib/Views/language/Plugin/views/field/Language.php @@ -0,0 +1,50 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['native_language'] = array( + '#title' => t('Native language'), + '#type' => 'checkbox', + '#default_value' => $this->options['native_language'], + '#description' => t('If enabled, the native name of the language will be displayed'), + ); + } + + function render($values) { + // @todo: Drupal Core dropped native language until config translation is + // ready, see http://drupal.org/node/1616594. + $value = $this->get_value($values); + $language = language_load($value); + return $language ? $language->name : ''; + } + +} diff --git a/core/modules/views/lib/Views/language/Plugin/views/filter/Language.php b/core/modules/views/lib/Views/language/Plugin/views/filter/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..294690c97017156e204d5f664e083da435e4ef42 --- /dev/null +++ b/core/modules/views/lib/Views/language/Plugin/views/filter/Language.php @@ -0,0 +1,38 @@ +value_options)) { + $this->value_title = t('Language'); + $languages = array( + '***CURRENT_LANGUAGE***' => t("Current user's language"), + '***DEFAULT_LANGUAGE***' => t("Default site language"), + LANGUAGE_NOT_SPECIFIED => t('No language') + ); + $languages = array_merge($languages, views_language_list()); + $this->value_options = $languages; + } + } + +} diff --git a/core/modules/views/lib/Views/locale/Plugin/views/field/LinkEdit.php b/core/modules/views/lib/Views/locale/Plugin/views/field/LinkEdit.php new file mode 100644 index 0000000000000000000000000000000000000000..55f891fb2c809c02eb98c5d815edb1ec1429edb9 --- /dev/null +++ b/core/modules/views/lib/Views/locale/Plugin/views/field/LinkEdit.php @@ -0,0 +1,77 @@ +additional_fields['lid'] = 'lid'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['text'] = array('default' => '', 'translatable' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + parent::buildOptionsForm($form, $form_state); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + public function access() { + // Ensure user has access to edit translations. + return user_access('translate interface'); + } + + function render($values) { + $value = $this->get_value($values, 'lid'); + return $this->render_link($this->sanitizeValue($value), $values); + } + + function render_link($data, $values) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'admin/build/translate/edit/' . $data; + $this->options['alter']['query'] = drupal_get_destination(); + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/locale/Plugin/views/filter/Version.php b/core/modules/views/lib/Views/locale/Plugin/views/filter/Version.php new file mode 100644 index 0000000000000000000000000000000000000000..a450317ea59a15ce080f62fc2b3241267d511fd9 --- /dev/null +++ b/core/modules/views/lib/Views/locale/Plugin/views/filter/Version.php @@ -0,0 +1,42 @@ +value_options)) { + $this->value_title = t('Version'); + // Enable filtering by the current installed Drupal version. + $versions = array('***CURRENT_VERSION***' => t('Current installed version')); + // Uses db_query() rather than db_select() because the query is static and + // does not include any variables. + $result = db_query('SELECT DISTINCT(version) FROM {locales_source} ORDER BY version'); + foreach ($result as $row) { + if (!empty($row->version)) { + $versions[$row->version] = $row->version; + } + } + $this->value_options = $versions; + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedDay.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedDay.php new file mode 100644 index 0000000000000000000000000000000000000000..0d6b4240feb82842773c6207584f302fd72dcfea --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedDay.php @@ -0,0 +1,55 @@ +formula = $this->extractSQL('DAY'); + return parent::get_formula(); + } + + /** + * Provide a link to the next level of the view + */ + function summary_name($data) { + $day = str_pad($data->{$this->name_alias}, 2, '0', STR_PAD_LEFT); + // strtotime respects server timezone, so we need to set the time fixed as utc time + return format_date(strtotime("2005" . "05" . $day . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + + /** + * Provide a link to the next level of the view + */ + function title() { + $day = str_pad($this->argument, 2, '0', STR_PAD_LEFT); + return format_date(strtotime("2005" . "05" . $day . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + + function summary_argument($data) { + // Make sure the argument contains leading zeroes. + return str_pad($data->{$this->base_alias}, 2, '0', STR_PAD_LEFT); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedFullDate.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedFullDate.php new file mode 100644 index 0000000000000000000000000000000000000000..ac3e1d9f5f06e8cbeff4889accd169bea1ee26a7 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedFullDate.php @@ -0,0 +1,48 @@ +formula = $this->getSQLFormat($this->definition['arg_format']); + return parent::get_formula(); + } + + /** + * Provide a link to the next level of the view + */ + function summary_name($data) { + $created = $data->{$this->name_alias}; + return format_date(strtotime($created . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + + /** + * Provide a link to the next level of the view + */ + function title() { + return format_date(strtotime($this->argument . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedMonth.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedMonth.php new file mode 100644 index 0000000000000000000000000000000000000000..22fb87fc36ca8158b0b7da3c43b0d608e4d7ffaa --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedMonth.php @@ -0,0 +1,54 @@ +formula = $this->extractSQL('MONTH'); + return parent::get_formula(); + } + + /** + * Provide a link to the next level of the view + */ + function summary_name($data) { + $month = str_pad($data->{$this->name_alias}, 2, '0', STR_PAD_LEFT); + return format_date(strtotime("2005" . $month . "15" . " 00:00:00 UTC" ), 'custom', $this->definition['format'], 'UTC'); + } + + /** + * Provide a link to the next level of the view + */ + function title() { + $month = str_pad($this->argument, 2, '0', STR_PAD_LEFT); + return format_date(strtotime("2005" . $month . "15" . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + + function summary_argument($data) { + // Make sure the argument contains leading zeroes. + return str_pad($data->{$this->base_alias}, 2, '0', STR_PAD_LEFT); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedWeek.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedWeek.php new file mode 100644 index 0000000000000000000000000000000000000000..e68c4681aaacf04048b5119a1a4a81b77e7ba1bc --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedWeek.php @@ -0,0 +1,40 @@ +formula = $this->extractSQL('WEEK'); + return parent::get_formula(); + } + + /** + * Provide a link to the next level of the view + */ + function summary_name($data) { + $created = $data->{$this->name_alias}; + return t('Week @week', array('@week' => $created)); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYear.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYear.php new file mode 100644 index 0000000000000000000000000000000000000000..fd92735bdaea3646ae36f792a5241f69df1a05e3 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYear.php @@ -0,0 +1,32 @@ +formula = $this->extractSQL('YEAR'); + return parent::get_formula(); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYearMonth.php b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYearMonth.php new file mode 100644 index 0000000000000000000000000000000000000000..5e0b8927254abc91b4ce6e77ab3bbb221abed45b --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYearMonth.php @@ -0,0 +1,48 @@ +formula = $this->getSQLFormat($this->definition['arg_format']); + return parent::get_formula(); + } + + /** + * Provide a link to the next level of the view + */ + function summary_name($data) { + $created = $data->{$this->name_alias}; + return format_date(strtotime($created . "15" . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + + /** + * Provide a link to the next level of the view + */ + function title() { + return format_date(strtotime($this->argument . "15" . " 00:00:00 UTC"), 'custom', $this->definition['format'], 'UTC'); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/Nid.php b/core/modules/views/lib/Views/node/Plugin/views/argument/Nid.php new file mode 100644 index 0000000000000000000000000000000000000000..a804363694e1303d3ae16c1cdfa03627f6d69d23 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/Nid.php @@ -0,0 +1,36 @@ +value); + foreach ($nodes as $node) { + $titles[] = check_plain($node->label()); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/Type.php b/core/modules/views/lib/Views/node/Plugin/views/argument/Type.php new file mode 100644 index 0000000000000000000000000000000000000000..f496cc81d0562da1fa646470c3f1c69d393e014b --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/Type.php @@ -0,0 +1,47 @@ +node_type($data->{$this->name_alias}); + } + + /** + * Override the behavior of title(). Get the user friendly version of the + * node type. + */ + function title() { + return $this->node_type($this->argument); + } + + function node_type($type) { + $output = node_type_get_label($type); + if (empty($output)) { + $output = t('Unknown content type'); + } + return check_plain($output); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/UidRevision.php b/core/modules/views/lib/Views/node/Plugin/views/argument/UidRevision.php new file mode 100644 index 0000000000000000000000000000000000000000..704f13137240183862a9aeff0f69e2e2bdab7323 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/UidRevision.php @@ -0,0 +1,30 @@ +ensureMyTable(); + $placeholder = $this->placeholder(); + $this->query->add_where_expression(0, "$this->tableAlias.uid = $placeholder OR ((SELECT COUNT(*) FROM {node_revision} nr WHERE nr.uid = $placeholder AND nr.nid = $this->tableAlias.nid) > 0)", array($placeholder => $this->argument)); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument/Vid.php b/core/modules/views/lib/Views/node/Plugin/views/argument/Vid.php new file mode 100644 index 0000000000000000000000000000000000000000..48376152a0b607e2a201ce8f623a4385f4d4d2d8 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument/Vid.php @@ -0,0 +1,51 @@ +fields('nr', array('vid', 'nid', 'title')) + ->condition('nr.vid', $this->value) + ->execute() + ->fetchAllAssoc('vid', PDO::FETCH_ASSOC); + $nids = array(); + foreach ($results as $result) { + $nids[] = $result['nid']; + } + + $nodes = node_load_multiple(array_unique($nids)); + + foreach ($results as $result) { + $nodes[$result['nid']]->set('title', $result['title']); + $titles[] = check_plain($nodes[$result['nid']]->label()); + } + + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument_default/Node.php b/core/modules/views/lib/Views/node/Plugin/views/argument_default/Node.php new file mode 100644 index 0000000000000000000000000000000000000000..ff32cd5aeb3ba49fd5066d614c31de7e7be0750b --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument_default/Node.php @@ -0,0 +1,40 @@ +nid; + } + } + + if (arg(0) == 'node' && is_numeric(arg(1))) { + return arg(1); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/argument_validator/Node.php b/core/modules/views/lib/Views/node/Plugin/views/argument_validator/Node.php new file mode 100644 index 0000000000000000000000000000000000000000..5066977410b8e50f8dd3aec75fd16c585a46a6f5 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/argument_validator/Node.php @@ -0,0 +1,144 @@ + array()); + $options['access'] = array('default' => FALSE, 'bool' => TRUE); + $options['access_op'] = array('default' => 'view'); + $options['nid_type'] = array('default' => 'nid'); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $types = node_type_get_types(); + $options = array(); + foreach ($types as $type => $info) { + $options[$type] = check_plain(t($info->name)); + } + + $form['types'] = array( + '#type' => 'checkboxes', + '#title' => t('Content types'), + '#options' => $options, + '#default_value' => $this->options['types'], + '#description' => t('Choose one or more content types to validate with.'), + ); + + $form['access'] = array( + '#type' => 'checkbox', + '#title' => t('Validate user has access to the content'), + '#default_value' => $this->options['access'], + ); + $form['access_op'] = array( + '#type' => 'radios', + '#title' => t('Access operation to check'), + '#options' => array('view' => t('View'), 'update' => t('Edit'), 'delete' => t('Delete')), + '#default_value' => $this->options['access_op'], + '#states' => array( + 'visible' => array( + ':input[name="options[validate][options][node][access]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['nid_type'] = array( + '#type' => 'select', + '#title' => t('Filter value format'), + '#options' => array( + 'nid' => t('Node ID'), + 'nids' => t('Node IDs separated by , or +'), + ), + '#default_value' => $this->options['nid_type'], + ); + } + + public function submitOptionsForm(&$form, &$form_state, &$options = array()) { + // filter trash out of the options so we don't store giant unnecessary arrays + $options['types'] = array_filter($options['types']); + } + + function validate_argument($argument) { + $types = $this->options['types']; + + switch ($this->options['nid_type']) { + case 'nid': + if (!is_numeric($argument)) { + return FALSE; + } + $node = node_load($argument); + if (!$node) { + return FALSE; + } + + if (!empty($this->options['access'])) { + if (!node_access($this->options['access_op'], $node)) { + return FALSE; + } + } + + // Save the title() handlers some work. + $this->argument->validated_title = check_plain($node->label()); + + if (empty($types)) { + return TRUE; + } + + return isset($types[$node->type]); + + case 'nids': + $nids = new stdClass(); + $nids->value = array($argument); + $nids = $this->breakPhrase($argument, $nids); + if ($nids->value == array(-1)) { + return FALSE; + } + + $test = drupal_map_assoc($nids->value); + $titles = array(); + + $nodes = node_load_multiple($nids->value); + foreach ($nodes as $node) { + if ($types && empty($types[$node->type])) { + return FALSE; + } + + if (!empty($this->options['access'])) { + if (!node_access($this->options['access_op'], $node)) { + return FALSE; + } + } + + $titles[] = check_plain($node->label()); + unset($test[$node->nid]); + } + + $this->argument->validated_title = implode($nids->operator == 'or' ? ' + ' : ', ', $titles); + // If this is not empty, we did not find a nid. + return empty($test); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/HistoryUserTimestamp.php b/core/modules/views/lib/Views/node/Plugin/views/field/HistoryUserTimestamp.php new file mode 100644 index 0000000000000000000000000000000000000000..c45d62be415d29a9a23692f59d804a87eaf1b256 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/HistoryUserTimestamp.php @@ -0,0 +1,95 @@ +uid) { + $this->additional_fields['created'] = array('table' => 'node', 'field' => 'created'); + $this->additional_fields['changed'] = array('table' => 'node', 'field' => 'changed'); + if (module_exists('comment') && !empty($this->options['comments'])) { + $this->additional_fields['last_comment'] = array('table' => 'node_comment_statistics', 'field' => 'last_comment_timestamp'); + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['comments'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + if (module_exists('comment')) { + $form['comments'] = array( + '#type' => 'checkbox', + '#title' => t('Check for new comments as well'), + '#default_value' => !empty($this->options['comments']), + '#fieldset' => 'more', + ); + } + } + + public function query() { + // Only add ourselves to the query if logged in. + global $user; + if (!$user->uid) { + return; + } + parent::query(); + } + + function render($values) { + // Let's default to 'read' state. + // This code shadows node_mark, but it reads from the db directly and + // we already have that info. + $mark = MARK_READ; + global $user; + if ($user->uid) { + $last_read = $this->get_value($values); + $changed = $this->get_value($values, 'changed'); + + $last_comment = module_exists('comment') && !empty($this->options['comments']) ? $this->get_value($values, 'last_comment') : 0; + + if (!$last_read && $changed > NODE_NEW_LIMIT) { + $mark = MARK_NEW; + } + elseif ($changed > $last_read && $changed > NODE_NEW_LIMIT) { + $mark = MARK_UPDATED; + } + elseif ($last_comment > $last_read && $last_comment > NODE_NEW_LIMIT) { + $mark = MARK_UPDATED; + } + return $this->render_link(theme('mark', array('type' => $mark)), $values); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Language.php b/core/modules/views/lib/Views/node/Plugin/views/field/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..c4dff1bdf8d98d4de7b188d50118ada93a547a11 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Language.php @@ -0,0 +1,51 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['native_language'] = array( + '#title' => t('Native language'), + '#type' => 'checkbox', + '#default_value' => $this->options['native_language'], + '#description' => t('If enabled, the native name of the language will be displayed'), + ); + } + + function render($values) { + // @todo: Drupal Core dropped native language until config translation is + // ready, see http://drupal.org/node/1616594. + $value = $this->get_value($values); + $language = language_load($value); + $value = $language ? $language->name : ''; + return $this->render_link($value, $values); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Link.php b/core/modules/views/lib/Views/node/Plugin/views/field/Link.php new file mode 100644 index 0000000000000000000000000000000000000000..0491889c2a197714d91e2af484b26f71fb306bfa --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Link.php @@ -0,0 +1,61 @@ + '', 'translatable' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + parent::buildOptionsForm($form, $form_state); + + // The path is set by render_link function so don't allow to set it. + $form['alter']['path'] = array('#access' => FALSE); + $form['alter']['external'] = array('#access' => FALSE); + } + + public function query() {} + + function render($values) { + if ($entity = $this->get_entity($values)) { + return $this->render_link($entity, $values); + } + } + + function render_link($node, $values) { + if (node_access('view', $node)) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/$node->nid"; + $text = !empty($this->options['text']) ? $this->options['text'] : t('view'); + return $text; + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/LinkDelete.php b/core/modules/views/lib/Views/node/Plugin/views/field/LinkDelete.php new file mode 100644 index 0000000000000000000000000000000000000000..f9d56719fb1316ea13f2343d2c720cd40cda23a7 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/LinkDelete.php @@ -0,0 +1,42 @@ +options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/$node->nid/delete"; + $this->options['alter']['query'] = drupal_get_destination(); + + $text = !empty($this->options['text']) ? $this->options['text'] : t('delete'); + return $text; + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/LinkEdit.php b/core/modules/views/lib/Views/node/Plugin/views/field/LinkEdit.php new file mode 100644 index 0000000000000000000000000000000000000000..1e59566b2999a813672c4b65a2d9577f5a37b550 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/LinkEdit.php @@ -0,0 +1,42 @@ +options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/$node->nid/edit"; + $this->options['alter']['query'] = drupal_get_destination(); + + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + return $text; + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Node.php b/core/modules/views/lib/Views/node/Plugin/views/field/Node.php new file mode 100644 index 0000000000000000000000000000000000000000..974c6be4d65f25548ec99a97b9ac27c099037d9f --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Node.php @@ -0,0 +1,92 @@ +options['link_to_node'])) { + $this->additional_fields['nid'] = array('table' => 'node', 'field' => 'nid'); + if (module_exists('translation')) { + $this->additional_fields['langcode'] = array('table' => 'node', 'field' => 'langcode'); + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_node'] = array('default' => isset($this->definition['link_to_node default']) ? $this->definition['link_to_node default'] : FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to node option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_node'] = array( + '#title' => t('Link this field to the original piece of content'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_node']), + ); + + parent::buildOptionsForm($form, $form_state); + } + + /** + * Render whatever the data is as a link to the node. + * + * Data should be made XSS safe prior to calling this function. + */ + function render_link($data, $values) { + if (!empty($this->options['link_to_node']) && !empty($this->additional_fields['nid'])) { + if ($data !== NULL && $data !== '') { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/" . $this->get_value($values, 'nid'); + if (isset($this->aliases['langcode'])) { + $languages = language_list(); + $langcode = $this->get_value($values, 'langcode'); + if (isset($languages[$langcode])) { + $this->options['alter']['language'] = $languages[$langcode]; + } + else { + unset($this->options['alter']['language']); + } + } + } + else { + $this->options['alter']['make_link'] = FALSE; + } + } + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Path.php b/core/modules/views/lib/Views/node/Plugin/views/field/Path.php new file mode 100644 index 0000000000000000000000000000000000000000..01a8c525d7d818c3b680f962d356457d6ae117a0 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Path.php @@ -0,0 +1,63 @@ +additional_fields['nid'] = 'nid'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['absolute'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['absolute'] = array( + '#type' => 'checkbox', + '#title' => t('Use absolute link (begins with "http://")'), + '#default_value' => $this->options['absolute'], + '#description' => t('Enable this option to output an absolute link. Required if you want to use the path as a link destination (as in "output this field as a link" above).'), + '#fieldset' => 'alter', + ); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + function render($values) { + $nid = $this->get_value($values, 'nid'); + return url("node/$nid", array('absolute' => $this->options['absolute'])); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Revision.php b/core/modules/views/lib/Views/node/Plugin/views/field/Revision.php new file mode 100644 index 0000000000000000000000000000000000000000..2e1b12a2a29ca24b6bc055f628c75cc53311e27e --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Revision.php @@ -0,0 +1,80 @@ +options['link_to_node_revision'])) { + $this->additional_fields['vid'] = 'vid'; + $this->additional_fields['nid'] = 'nid'; + if (module_exists('translation')) { + $this->additional_fields['langcode'] = array('table' => 'node', 'field' => 'langcode'); + } + } + } + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_node_revision'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to revision option. + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_node_revision'] = array( + '#title' => t('Link this field to its content revision'), + '#description' => t('This will override any other link you have set.'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_node_revision']), + ); + parent::buildOptionsForm($form, $form_state); + } + + /** + * Render whatever the data is as a link to the node. + * + * Data should be made XSS safe prior to calling this function. + */ + function render_link($data, $values) { + if (!empty($this->options['link_to_node_revision']) && $data !== NULL && $data !== '') { + $this->options['alter']['make_link'] = TRUE; + $nid = $this->get_value($values, 'nid'); + $vid = $this->get_value($values, 'vid'); + $this->options['alter']['path'] = "node/" . $nid . '/revisions/' . $vid . '/view'; + if (module_exists('translation')) { + $langcode = $this->get_value($values, 'langcode'); + $languages = language_list(); + if (isset($languages[$langcode])) { + $this->options['alter']['langcode'] = $languages[$langcode]; + } + } + } + else { + return parent::render_link($data, $values); + } + return $data; + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLink.php b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLink.php new file mode 100644 index 0000000000000000000000000000000000000000..8f9c2b31c269651fc1fe78a98591c8cf9c7f52a1 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLink.php @@ -0,0 +1,82 @@ +additional_fields['node_vid'] = array('table' => 'node_revision', 'field' => 'vid'); + } + + public function access() { + return user_access('view revisions') || user_access('administer nodes'); + } + + function render_link($data, $values) { + list($node, $vid) = $this->get_revision_entity($values, 'view'); + if (!isset($vid)) { + return; + } + + // Current revision uses the node view path. + $path = 'node/' . $node->nid; + if ($node->vid != $vid) { + $path .= "/revisions/$vid/view"; + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = $path; + $this->options['alter']['query'] = drupal_get_destination(); + + return !empty($this->options['text']) ? $this->options['text'] : t('view'); + } + + /** + * Returns the revision values of a node. + * + * @param object $values + * An object containing all retrieved values. + * @param string $op + * The operation being performed. + * + * @return array + * A numerically indexed array containing the current node object and the + * revision ID for this row. + */ + function get_revision_entity($values, $op) { + $vid = $this->get_value($values, 'node_vid'); + $node = $this->get_value($values); + // Unpublished nodes ignore access control. + $node->status = 1; + // Ensure user has access to perform the operation on this node. + if (!node_access($op, $node)) { + return array($node, NULL); + } + return array($node, $vid); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkDelete.php b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkDelete.php new file mode 100644 index 0000000000000000000000000000000000000000..ecb9208408002ec0319f53c32486065961056fe1 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkDelete.php @@ -0,0 +1,47 @@ +get_revision_entity($values, 'delete'); + if (!isset($vid)) { + return; + } + + // Current revision cannot be deleted. + if ($node->vid == $vid) { + return; + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'node/' . $node->nid . "/revisions/$vid/delete"; + $this->options['alter']['query'] = drupal_get_destination(); + + return !empty($this->options['text']) ? $this->options['text'] : t('delete'); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkRevert.php b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkRevert.php new file mode 100644 index 0000000000000000000000000000000000000000..bad0e2ec95a25661da265087d24c4d6ce6d72f44 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkRevert.php @@ -0,0 +1,47 @@ +get_revision_entity($values, 'update'); + if (!isset($vid)) { + return; + } + + // Current revision cannot be reverted. + if ($node->vid == $vid) { + return; + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'node/' . $node->nid . "/revisions/$vid/revert"; + $this->options['alter']['query'] = drupal_get_destination(); + + return !empty($this->options['text']) ? $this->options['text'] : t('revert'); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/field/Type.php b/core/modules/views/lib/Views/node/Plugin/views/field/Type.php new file mode 100644 index 0000000000000000000000000000000000000000..38f7f1aadc9f4c558cf527cbe69f92deb3090530 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/field/Type.php @@ -0,0 +1,61 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + /** + * Provide machine_name option for to node type display. + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['machine_name'] = array( + '#title' => t('Output machine name'), + '#description' => t('Display field as the content type machine name.'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['machine_name']), + ); + } + + /** + * Render node type as human readable name, unless using machine_name option. + */ + function render_name($data, $values) { + if ($this->options['machine_name'] != 1 && $data !== NULL && $data !== '') { + return t($this->sanitizeValue(node_type_get_label($data))); + } + return $this->sanitizeValue($data); + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->render_name($value, $values), $values); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/filter/Access.php b/core/modules/views/lib/Views/node/Plugin/views/filter/Access.php new file mode 100644 index 0000000000000000000000000000000000000000..a532c50879feb5e997798b491788ea7401a613f3 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/filter/Access.php @@ -0,0 +1,52 @@ +ensureMyTable(); + $grants = db_or(); + foreach (node_access_grants('view') as $realm => $gids) { + foreach ($gids as $gid) { + $grants->condition(db_and() + ->condition($table . '.gid', $gid) + ->condition($table . '.realm', $realm) + ); + } + } + + $this->query->add_where('AND', $grants); + $this->query->add_where('AND', $table . '.grant_view', 1, '>='); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/filter/HistoryUserTimestamp.php b/core/modules/views/lib/Views/node/Plugin/views/filter/HistoryUserTimestamp.php new file mode 100644 index 0000000000000000000000000000000000000000..7e9c61323c30c3f9878a47bdc798c3dd2c34bdfc --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/filter/HistoryUserTimestamp.php @@ -0,0 +1,99 @@ +options['expose']['label'])) { + $label = $this->options['expose']['label']; + } + else { + $label = t('Has new content'); + } + $form['value'] = array( + '#type' => 'checkbox', + '#title' => $label, + '#default_value' => $this->value, + ); + } + } + + public function query() { + global $user; + // This can only work if we're logged in. + if (!$user || !$user->uid) { + return; + } + + // Don't filter if we're exposed and the checkbox isn't selected. + if ((!empty($this->options['exposed'])) && empty($this->value)) { + return; + } + + // Hey, Drupal kills old history, so nodes that haven't been updated + // since NODE_NEW_LIMIT are bzzzzzzzt outta here! + + $limit = REQUEST_TIME - NODE_NEW_LIMIT; + + $this->ensureMyTable(); + $field = "$this->tableAlias.$this->realField"; + $node = $this->query->ensure_table('node', $this->relationship); + + $clause = ''; + $clause2 = ''; + if (module_exists('comment')) { + $ncs = $this->query->ensure_table('node_comment_statistics', $this->relationship); + $clause = ("OR $ncs.last_comment_timestamp > (***CURRENT_TIME*** - $limit)"); + $clause2 = "OR $field < $ncs.last_comment_timestamp"; + } + + // NULL means a history record doesn't exist. That's clearly new content. + // Unless it's very very old content. Everything in the query is already + // type safe cause none of it is coming from outside here. + $this->query->add_where_expression($this->options['group'], "($field IS NULL AND ($node.changed > (***CURRENT_TIME*** - $limit) $clause)) OR $field < $node.changed $clause2"); + } + + public function adminSummary() { + if (!empty($this->options['exposed'])) { + return t('exposed'); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/filter/Status.php b/core/modules/views/lib/Views/node/Plugin/views/filter/Status.php new file mode 100644 index 0000000000000000000000000000000000000000..3297ae6c7d3efe45315d2d2e765f2df93c8896f2 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/filter/Status.php @@ -0,0 +1,36 @@ +ensureMyTable(); + $this->query->add_where_expression($this->options['group'], "$table.status = 1 OR ($table.uid = ***CURRENT_USER*** AND ***CURRENT_USER*** <> 0 AND ***VIEW_OWN_UNPUBLISHED_NODES*** = 1) OR ***BYPASS_NODE_ACCESS*** = 1"); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/filter/Type.php b/core/modules/views/lib/Views/node/Plugin/views/filter/Type.php new file mode 100644 index 0000000000000000000000000000000000000000..19657fbdff3b76b8d942e7e9aa08f7e255df86b6 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/filter/Type.php @@ -0,0 +1,38 @@ +value_options)) { + $this->value_title = t('Content types'); + $types = node_type_get_types(); + $options = array(); + foreach ($types as $type => $info) { + $options[$type] = t($info->name); + } + asort($options); + $this->value_options = $options; + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/filter/UidRevision.php b/core/modules/views/lib/Views/node/Plugin/views/filter/UidRevision.php new file mode 100644 index 0000000000000000000000000000000000000000..c33818ecd924f6b2ee8d469fc76f22f57389bdd2 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/filter/UidRevision.php @@ -0,0 +1,37 @@ +ensureMyTable(); + + $placeholder = $this->placeholder(); + + $args = array_values($this->value); + + $this->query->add_where_expression($this->options['group'], "$this->tableAlias.uid IN($placeholder) OR + ((SELECT COUNT(*) FROM {node_revision} nr WHERE nr.uid IN($placeholder) AND nr.nid = $this->tableAlias.nid) > 0)", array($placeholder => $args), + $args); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/row/Rss.php b/core/modules/views/lib/Views/node/Plugin/views/row/Rss.php new file mode 100644 index 0000000000000000000000000000000000000000..0d5a8df31bfc9cddaedc344039205614ccbd49a0 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/row/Rss.php @@ -0,0 +1,181 @@ + 'default'); + $options['links'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['item_length'] = array( + '#type' => 'select', + '#title' => t('Display type'), + '#options' => $this->buildOptionsForm_summary_options(), + '#default_value' => $this->options['item_length'], + ); + $form['links'] = array( + '#type' => 'checkbox', + '#title' => t('Display links'), + '#default_value' => $this->options['links'], + ); + } + + /** + * Return the main options, which are shown in the summary title. + */ + public function buildOptionsForm_summary_options() { + $entity_info = entity_get_info('node'); + $options = array(); + if (!empty($entity_info['view modes'])) { + foreach ($entity_info['view modes'] as $mode => $settings) { + $options[$mode] = $settings['label']; + } + } + $options['title'] = t('Title only'); + $options['default'] = t('Use site default RSS settings'); + return $options; + } + + public function summaryTitle() { + $options = $this->buildOptionsForm_summary_options(); + return check_plain($options[$this->options['item_length']]); + } + + function pre_render($values) { + $nids = array(); + foreach ($values as $row) { + $nids[] = $row->{$this->field_alias}; + } + if (!empty($nids)) { + $this->nodes = node_load_multiple($nids); + } + } + + function render($row) { + // For the most part, this code is taken from node_feed() in node.module + global $base_url; + + $nid = $row->{$this->field_alias}; + if (!is_numeric($nid)) { + return; + } + + $display_mode = $this->options['item_length']; + if ($display_mode == 'default') { + $display_mode = config('system.rss')->get('items.view_mode'); + } + + // Load the specified node: + $node = $this->nodes[$nid]; + if (empty($node)) { + return; + } + + $item_text = ''; + + $uri = $node->uri(); + $node->link = url($uri['path'], $uri['options'] + array('absolute' => TRUE)); + $node->rss_namespaces = array(); + $node->rss_elements = array( + array( + 'key' => 'pubDate', + 'value' => gmdate('r', $node->created), + ), + array( + 'key' => 'dc:creator', + 'value' => $node->name, + ), + array( + 'key' => 'guid', + 'value' => $node->nid . ' at ' . $base_url, + 'attributes' => array('isPermaLink' => 'false'), + ), + ); + + // The node gets built and modules add to or modify $node->rss_elements + // and $node->rss_namespaces. + + $build_mode = $display_mode; + + $build = node_view($node, $build_mode); + unset($build['#theme']); + + if (!empty($node->rss_namespaces)) { + $this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $node->rss_namespaces); + } + elseif (function_exists('rdf_get_namespaces')) { + // Merge RDF namespaces in the XML namespaces in case they are used + // further in the RSS content. + $xml_rdf_namespaces = array(); + foreach (rdf_get_namespaces() as $prefix => $uri) { + $xml_rdf_namespaces['xmlns:' . $prefix] = $uri; + } + $this->view->style_plugin->namespaces += $xml_rdf_namespaces; + } + + // Hide the links if desired. + if (!$this->options['links']) { + hide($build['links']); + } + + if ($display_mode != 'title') { + // We render node contents and force links to be last. + $build['links']['#weight'] = 1000; + $item_text .= drupal_render($build); + } + + $item = new \stdClass(); + $item->description = $item_text; + $item->title = $node->label(); + $item->link = $node->link; + $item->elements = $node->rss_elements; + $item->nid = $node->nid; + + return theme($this->themeFunctions(), array( + 'view' => $this->view, + 'options' => $this->options, + 'row' => $item + )); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/row/View.php b/core/modules/views/lib/Views/node/Plugin/views/row/View.php new file mode 100644 index 0000000000000000000000000000000000000000..ef7cb17bdcb7c67c13f73f9803be3eac3d4ecd60 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/row/View.php @@ -0,0 +1,66 @@ + TRUE, 'bool' => TRUE); + $options['comments'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + /** + * Overrides Views\system\Plugin\views\row\Entity::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['links'] = array( + '#type' => 'checkbox', + '#title' => t('Display links'), + '#default_value' => $this->options['links'], + ); + $form['comments'] = array( + '#type' => 'checkbox', + '#title' => t('Display comments'), + '#default_value' => $this->options['comments'], + ); + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/wizard/Node.php b/core/modules/views/lib/Views/node/Plugin/views/wizard/Node.php new file mode 100644 index 0000000000000000000000000000000000000000..74da6acb377113b375d3ce6a54644582fbe488a6 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/wizard/Node.php @@ -0,0 +1,316 @@ + 'nid', + 'table' => 'node', + 'field' => 'nid', + 'exclude' => TRUE, + 'link_to_node' => FALSE, + 'alter' => array( + 'alter_text' => TRUE, + 'text' => 'node/[nid]' + ) + ); + + /** + * Set default values for the filters. + */ + protected $filters = array( + 'status' => array( + 'value' => TRUE, + 'table' => 'node', + 'field' => 'status' + ) + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::getAvailableSorts(). + * + * @return array + */ + public function getAvailableSorts() { + // You can't execute functions in properties, so override the method + return array( + 'title:DESC' => t('Title') + ); + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::row_style_options(). + */ + protected function row_style_options() { + $options = array(); + $options['teasers'] = t('teasers'); + $options['full_posts'] = t('full posts'); + $options['titles'] = t('titles'); + $options['titles_linked'] = t('titles (linked)'); + $options['fields'] = t('fields'); + return $options; + } + + /** + * Adds the style options to the wizard form. + * + * @param array $form + * The full wizard form array. + * @param array $form_state + * The current state of the wizard form. + * @param string $type + * The display ID (e.g. 'page' or 'block'). + */ + protected function build_form_style(array &$form, array &$form_state, $type) { + parent::build_form_style($form, $form_state, $type); + $style_form =& $form['displays'][$type]['options']['style']; + // Some style plugins don't support row plugins so stop here if that's the + // case. + if (!isset($style_form['row_plugin']['#default_value'])) { + return; + } + $row_plugin = $style_form['row_plugin']['#default_value']; + switch ($row_plugin) { + case 'full_posts': + case 'teasers': + $style_form['row_options']['links'] = array( + '#type' => 'select', + '#title_display' => 'invisible', + '#title' => t('Should links be displayed below each node'), + '#options' => array( + 1 => t('with links (allow users to add comments, etc.)'), + 0 => t('without links'), + ), + '#default_value' => 1, + ); + $style_form['row_options']['comments'] = array( + '#type' => 'select', + '#title_display' => 'invisible', + '#title' => t('Should comments be displayed below each node'), + '#options' => array( + 1 => t('with comments'), + 0 => t('without comments'), + ), + '#default_value' => 0, + ); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + // Add the title field, so that the display has content if the user switches + // to a row style that uses fields. + /* Field: Content: Title */ + $display_options['fields']['title']['id'] = 'title'; + $display_options['fields']['title']['table'] = 'node'; + $display_options['fields']['title']['field'] = 'title'; + $display_options['fields']['title']['label'] = ''; + $display_options['fields']['title']['alter']['alter_text'] = 0; + $display_options['fields']['title']['alter']['make_link'] = 0; + $display_options['fields']['title']['alter']['absolute'] = 0; + $display_options['fields']['title']['alter']['trim'] = 0; + $display_options['fields']['title']['alter']['word_boundary'] = 0; + $display_options['fields']['title']['alter']['ellipsis'] = 0; + $display_options['fields']['title']['alter']['strip_tags'] = 0; + $display_options['fields']['title']['alter']['html'] = 0; + $display_options['fields']['title']['hide_empty'] = 0; + $display_options['fields']['title']['empty_zero'] = 0; + $display_options['fields']['title']['link_to_node'] = 1; + + return $display_options; + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_filters_user(). + */ + protected function default_display_filters_user(array $form, array &$form_state) { + $filters = parent::default_display_filters_user($form, $form_state); + + if (!empty($form_state['values']['show']['tagged_with']['tids'])) { + $filters['tid'] = array( + 'id' => 'tid', + 'table' => 'taxonomy_index', + 'field' => 'tid', + 'value' => $form_state['values']['show']['tagged_with']['tids'], + 'vocabulary' => $form_state['values']['show']['tagged_with']['vocabulary'], + ); + // If the user entered more than one valid term in the autocomplete + // field, they probably intended both of them to be applied. + if (count($form_state['values']['show']['tagged_with']['tids']) > 1) { + $filters['tid']['operator'] = 'and'; + // Sort the terms so the filter will be displayed as it normally would + // on the edit screen. + sort($filters['tid']['value']); + } + } + + return $filters; + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::page_display_options(). + */ + protected function page_display_options(array $form, array &$form_state) { + $display_options = parent::page_display_options($form, $form_state); + $row_plugin = isset($form_state['values']['page']['style']['row_plugin']) ? $form_state['values']['page']['style']['row_plugin'] : NULL; + $row_options = isset($form_state['values']['page']['style']['row_options']) ? $form_state['values']['page']['style']['row_options'] : array(); + $this->display_options_row($display_options, $row_plugin, $row_options); + return $display_options; + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::page_display_options(). + */ + protected function block_display_options(array $form, array &$form_state) { + $display_options = parent::block_display_options($form, $form_state); + $row_plugin = isset($form_state['values']['block']['style']['row_plugin']) ? $form_state['values']['block']['style']['row_plugin'] : NULL; + $row_options = isset($form_state['values']['block']['style']['row_options']) ? $form_state['values']['block']['style']['row_options'] : array(); + $this->display_options_row($display_options, $row_plugin, $row_options); + return $display_options; + } + + /** + * Set the row style and row style plugins to the display_options. + */ + protected function display_options_row(&$display_options, $row_plugin, $row_options) { + switch ($row_plugin) { + case 'full_posts': + $display_options['row']['type'] = 'node'; + $display_options['row']['options']['build_mode'] = 'full'; + $display_options['row']['options']['links'] = !empty($row_options['links']); + $display_options['row']['options']['comments'] = !empty($row_options['comments']); + break; + case 'teasers': + $display_options['row']['type'] = 'node'; + $display_options['row']['options']['build_mode'] = 'teaser'; + $display_options['row']['options']['links'] = !empty($row_options['links']); + $display_options['row']['options']['comments'] = !empty($row_options['comments']); + break; + case 'titles_linked': + $display_options['row']['type'] = 'fields'; + $display_options['field']['title']['link_to_node'] = 1; + break; + case 'titles': + $display_options['row']['type'] = 'fields'; + $display_options['field']['title']['link_to_node'] = 0; + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::build_filters(). + * + * Add some options for filter by taxonomy terms. + */ + protected function build_filters(&$form, &$form_state) { + parent::build_filters($form, $form_state); + $entity_info = $this->entity_info; + + $selected_bundle = views_ui_get_selected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']); + + // Add the "tagged with" filter to the view. + + // We construct this filter using taxonomy_index.tid (which limits the + // filtering to a specific vocabulary) rather than taxonomy_term_data.name + // (which matches terms in any vocabulary). This is because it is a more + // commonly-used filter that works better with the autocomplete UI, and + // also to avoid confusion with other vocabularies on the site that may + // have terms with the same name but are not used for free tagging. + + // The downside is that if there *is* more than one vocabulary on the site + // that is used for free tagging, the wizard will only be able to make the + // "tagged with" filter apply to one of them (see below for the method it + // uses to choose). + + // Find all "tag-like" taxonomy fields associated with the view's + // entities. If a particular entity type (i.e., bundle) has been + // selected above, then we only search for taxonomy fields associated + // with that bundle. Otherwise, we use all bundles. + $bundles = array_keys($entity_info['bundles']); + // Double check that this is a real bundle before using it (since above + // we added a dummy option 'all' to the bundle list on the form). + if (isset($selected_bundle) && in_array($selected_bundle, $bundles)) { + $bundles = array($selected_bundle); + } + $tag_fields = array(); + foreach ($bundles as $bundle) { + foreach (field_info_instances($this->entity_type, $bundle) as $instance) { + // We define "tag-like" taxonomy fields as ones that use the + // "Autocomplete term widget (tagging)" widget. + if ($instance['widget']['type'] == 'taxonomy_autocomplete') { + $tag_fields[] = $instance['field_name']; + } + } + } + $tag_fields = array_unique($tag_fields); + if (!empty($tag_fields)) { + // If there is more than one "tag-like" taxonomy field available to + // the view, we can only make our filter apply to one of them (as + // described above). We choose 'field_tags' if it is available, since + // that is created by the Standard install profile in core and also + // commonly used by contrib modules; thus, it is most likely to be + // associated with the "main" free-tagging vocabulary on the site. + if (in_array('field_tags', $tag_fields)) { + $tag_field_name = 'field_tags'; + } + else { + $tag_field_name = reset($tag_fields); + } + // Add the autocomplete textfield to the wizard. + $form['displays']['show']['tagged_with'] = array( + '#type' => 'textfield', + '#title' => t('tagged with'), + '#autocomplete_path' => 'taxonomy/autocomplete/' . $tag_field_name, + '#size' => 30, + '#maxlength' => 1024, + '#field_name' => $tag_field_name, + '#element_validate' => array('views_ui_taxonomy_autocomplete_validate'), + ); + } + } + +} diff --git a/core/modules/views/lib/Views/node/Plugin/views/wizard/NodeRevision.php b/core/modules/views/lib/Views/node/Plugin/views/wizard/NodeRevision.php new file mode 100644 index 0000000000000000000000000000000000000000..8e3f3c388681a197a617103434f57de1aca2b5a2 --- /dev/null +++ b/core/modules/views/lib/Views/node/Plugin/views/wizard/NodeRevision.php @@ -0,0 +1,134 @@ + 'vid', + 'table' => 'node_revision', + 'field' => 'vid', + 'exclude' => TRUE, + 'alter' => array( + 'alter_text' => TRUE, + 'text' => 'node/[nid]/revisions/[vid]/view' + ) + ); + + /** + * Set the additional information for the pathField property. + */ + protected $pathFieldsSupplemental = array( + array( + 'id' => 'nid', + 'table' => 'node', + 'field' => 'nid', + 'exclude' => TRUE, + 'link_to_node' => FALSE + ) + ); + + /** + * Set default values for the filters. + */ + protected $filters = array( + 'status' => array( + 'value' => TRUE, + 'table' => 'node_revision', + 'field' => 'status' + ) + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::row_style_options(). + * + * Node revisions do not support full posts or teasers, so remove them. + */ + protected function row_style_options() { + $options = parent::row_style_options(); + unset($options['teasers']); + unset($options['full_posts']); + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + $display_options['access']['perm'] = 'view revisions'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: Content revision: Created date */ + $display_options['fields']['timestamp']['id'] = 'timestamp'; + $display_options['fields']['timestamp']['table'] = 'node_revision'; + $display_options['fields']['timestamp']['field'] = 'timestamp'; + $display_options['fields']['timestamp']['alter']['alter_text'] = 0; + $display_options['fields']['timestamp']['alter']['make_link'] = 0; + $display_options['fields']['timestamp']['alter']['absolute'] = 0; + $display_options['fields']['timestamp']['alter']['trim'] = 0; + $display_options['fields']['timestamp']['alter']['word_boundary'] = 0; + $display_options['fields']['timestamp']['alter']['ellipsis'] = 0; + $display_options['fields']['timestamp']['alter']['strip_tags'] = 0; + $display_options['fields']['timestamp']['alter']['html'] = 0; + $display_options['fields']['timestamp']['hide_empty'] = 0; + $display_options['fields']['timestamp']['empty_zero'] = 0; + + /* Field: Content revision: Title */ + $display_options['fields']['title']['id'] = 'title'; + $display_options['fields']['title']['table'] = 'node_revision'; + $display_options['fields']['title']['field'] = 'title'; + $display_options['fields']['title']['label'] = ''; + $display_options['fields']['title']['alter']['alter_text'] = 0; + $display_options['fields']['title']['alter']['make_link'] = 0; + $display_options['fields']['title']['alter']['absolute'] = 0; + $display_options['fields']['title']['alter']['trim'] = 0; + $display_options['fields']['title']['alter']['word_boundary'] = 0; + $display_options['fields']['title']['alter']['ellipsis'] = 0; + $display_options['fields']['title']['alter']['strip_tags'] = 0; + $display_options['fields']['title']['alter']['html'] = 0; + $display_options['fields']['title']['hide_empty'] = 0; + $display_options['fields']['title']['empty_zero'] = 0; + $display_options['fields']['title']['link_to_node'] = 0; + $display_options['fields']['title']['link_to_node_revision'] = 1; + + return $display_options; + } + +} diff --git a/core/modules/views/lib/Views/search/Plugin/views/argument/Search.php b/core/modules/views/lib/Views/search/Plugin/views/argument/Search.php new file mode 100644 index 0000000000000000000000000000000000000000..04091f663917d54f4936a6d8fedc261490f12b04 --- /dev/null +++ b/core/modules/views/lib/Views/search/Plugin/views/argument/Search.php @@ -0,0 +1,116 @@ +search_query)) { + $this->search_query = db_select('search_index', 'i', array('target' => 'slave'))->extend('Views\search\ViewsSearchQuery'); + $this->search_query->searchExpression($input, $this->view->base_table); + $this->search_query->publicParseSearchExpression(); + } + } + + /** + * Add this argument to the query. + */ + public function query($group_by = FALSE) { + $required = FALSE; + $this->query_parse_search_expression($this->argument); + if (!isset($this->search_query)) { + $required = TRUE; + } + else { + $words = $this->search_query->words(); + if (empty($words)) { + $required = TRUE; + } + } + if ($required) { + if ($this->operator == 'required') { + $this->query->add_where(0, 'FALSE'); + } + } + else { + $search_index = $this->ensureMyTable(); + + $search_condition = db_and(); + + // Create a new join to relate the 'search_total' table to our current 'search_index' table. + $definition = array( + 'table' => 'search_total', + 'field' => 'word', + 'left_table' => $search_index, + 'left_field' => 'word', + ); + $join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition); + $search_total = $this->query->add_relationship('search_total', $join, $search_index); + + $this->search_score = $this->query->add_field('', "SUM($search_index.score * $search_total.count)", 'score', array('aggregate' => TRUE)); + + if (empty($this->query->relationships[$this->relationship])) { + $base_table = $this->query->base_table; + } + else { + $base_table = $this->query->relationships[$this->relationship]['base']; + } + $search_condition->condition("$search_index.type", $base_table); + + if (!$this->search_query->simple()) { + $search_dataset = $this->query->add_table('search_dataset'); + $conditions = $this->search_query->conditions(); + $condition_conditions =& $conditions->conditions(); + foreach ($condition_conditions as $key => &$condition) { + // Take sure we just look at real conditions. + if (is_numeric($key)) { + // Replace the conditions with the table alias of views. + $this->search_query->condition_replace_string('d.', "$search_dataset.", $condition); + } + } + $search_conditions =& $search_condition->conditions(); + $search_conditions = array_merge($search_conditions, $condition_conditions); + } + else { + // Stores each condition, so and/or on the filter level will still work. + $or = db_or(); + foreach ($words as $word) { + $or->condition("$search_index.word", $word); + } + + $search_condition->condition($or); + } + + $this->query->add_where(0, $search_condition); + $this->query->add_groupby("$search_index.sid"); + $matches = $this->search_query->matches(); + $placeholder = $this->placeholder(); + $this->query->add_having_expression(0, "COUNT(*) >= $placeholder", array($placeholder => $matches)); + } + } + +} diff --git a/core/modules/views/lib/Views/search/Plugin/views/field/Score.php b/core/modules/views/lib/Views/search/Plugin/views/field/Score.php new file mode 100644 index 0000000000000000000000000000000000000000..d8bfaef319b0fbc6ff4738453cb302b8799988fe --- /dev/null +++ b/core/modules/views/lib/Views/search/Plugin/views/field/Score.php @@ -0,0 +1,93 @@ + ''); + $options['alternate_order'] = array('default' => 'asc'); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $style_options = $this->view->display_handler->getOption('style_options'); + if (isset($style_options['default']) && $style_options['default'] == $this->options['id']) { + $handlers = $this->view->display_handler->getHandlers('field'); + $options = array('' => t('No alternate')); + foreach ($handlers as $id => $handler) { + $options[$id] = $handler->adminLabel(); + } + + $form['alternate_sort'] = array( + '#type' => 'select', + '#title' => t('Alternative sort'), + '#description' => t('Pick an alternative default table sort field to use when the search score field is unavailable.'), + '#options' => $options, + '#default_value' => $this->options['alternate_sort'], + ); + + $form['alternate_order'] = array( + '#type' => 'select', + '#title' => t('Alternate sort order'), + '#options' => array('asc' => t('Ascending'), 'desc' => t('Descending')), + '#default_value' => $this->options['alternate_order'], + ); + } + + parent::buildOptionsForm($form, $form_state); + } + + public function query() { + // Check to see if the search filter added 'score' to the table. + // Our filter stores it as $handler->search_score -- and we also + // need to check its relationship to make sure that we're using the same + // one or obviously this won't work. + foreach ($this->view->filter as $handler) { + if (isset($handler->search_score) && $handler->relationship == $this->relationship) { + $this->field_alias = $handler->search_score; + $this->tableAlias = $handler->tableAlias; + return; + } + } + + // Hide this field if no search filter is in place. + $this->options['exclude'] = TRUE; + if (!empty($this->options['alternate_sort'])) { + if (isset($this->view->style_plugin->options['default']) && $this->view->style_plugin->options['default'] == $this->options['id']) { + // Since the style handler initiates fields, we plug these values right into the active handler. + $this->view->style_plugin->options['default'] = $this->options['alternate_sort']; + $this->view->style_plugin->options['order'] = $this->options['alternate_order']; + } + } + } + + function render($values) { + // Only render if we exist. + if (isset($this->tableAlias)) { + return parent::render($values); + } + } + +} diff --git a/core/modules/views/lib/Views/search/Plugin/views/filter/Search.php b/core/modules/views/lib/Views/search/Plugin/views/filter/Search.php new file mode 100644 index 0000000000000000000000000000000000000000..a35e94230054c619a4e116a7ee71660592d97520 --- /dev/null +++ b/core/modules/views/lib/Views/search/Plugin/views/filter/Search.php @@ -0,0 +1,198 @@ + 'radios', + '#title' => t('On empty input'), + '#default_value' => $this->operator, + '#options' => array( + 'optional' => t('Show All'), + 'required' => t('Show None'), + ), + ); + } + + /** + * Provide a simple textfield for equality + */ + function value_form(&$form, &$form_state) { + $form['value'] = array( + '#type' => 'textfield', + '#size' => 15, + '#default_value' => $this->value, + '#attributes' => array('title' => t('Enter the terms you wish to search for.')), + '#title' => empty($form_state['exposed']) ? t('Value') : '', + ); + } + + /** + * Validate the options form. + */ + public function validateExposed(&$form, &$form_state) { + if (!isset($this->options['expose']['identifier'])) { + return; + } + + $key = $this->options['expose']['identifier']; + if (!empty($form_state['values'][$key])) { + $this->query_parse_search_expression($form_state['values'][$key]); + if (count($this->search_query->words()) == 0) { + form_set_error($key, format_plural(config('search.settings')->get('index.minimum_word_size'), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.')); + } + } + } + + /** + * Take sure that parseSearchExpression is runned and everything is set up for it. + * + * @param $input + * The search phrase which was input by the user. + */ + function query_parse_search_expression($input) { + if (!isset($this->search_query)) { + $this->parsed = TRUE; + $this->search_query = db_select('search_index', 'i', array('target' => 'slave'))->extend('Views\search\ViewsSearchQuery'); + $this->search_query->searchExpression($input, $this->view->base_table); + $this->search_query->publicParseSearchExpression(); + } + } + + /** + * Add this filter to the query. + * + * Due to the nature of fapi, the value and the operator have an unintended + * level of indirection. You will find them in $this->operator + * and $this->value respectively. + */ + public function query() { + // Since attachment views don't validate the exposed input, parse the search + // expression if required. + if (!$this->parsed) { + $this->query_parse_search_expression($this->value); + } + $required = FALSE; + if (!isset($this->search_query)) { + $required = TRUE; + } + else { + $words = $this->search_query->words(); + if (empty($words)) { + $required = TRUE; + } + } + if ($required) { + if ($this->operator == 'required') { + $this->query->add_where($this->options['group'], 'FALSE'); + } + } + else { + $search_index = $this->ensureMyTable(); + + $search_condition = db_and(); + + // Create a new join to relate the 'serach_total' table to our current 'search_index' table. + $definition = array( + 'table' => 'search_total', + 'field' => 'word', + 'left_table' => $search_index, + 'left_field' => 'word', + ); + $join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition); + + $search_total = $this->query->add_relationship('search_total', $join, $search_index); + + $this->search_score = $this->query->add_field('', "SUM($search_index.score * $search_total.count)", 'score', array('aggregate' => TRUE)); + + if (empty($this->query->relationships[$this->relationship])) { + $base_table = $this->query->base_table; + } + else { + $base_table = $this->query->relationships[$this->relationship]['base']; + } + $search_condition->condition("$search_index.type", $base_table); + if (!$this->search_query->simple()) { + $search_dataset = $this->query->add_table('search_dataset'); + $conditions = $this->search_query->conditions(); + $condition_conditions =& $conditions->conditions(); + foreach ($condition_conditions as $key => &$condition) { + // Take sure we just look at real conditions. + if (is_numeric($key)) { + // Replace the conditions with the table alias of views. + $this->search_query->condition_replace_string('d.', "$search_dataset.", $condition); + } + } + $search_conditions =& $search_condition->conditions(); + $search_conditions = array_merge($search_conditions, $condition_conditions); + } + else { + // Stores each condition, so and/or on the filter level will still work. + $or = db_or(); + foreach ($words as $word) { + $or->condition("$search_index.word", $word); + } + + $search_condition->condition($or); + } + + $this->query->add_where($this->options['group'], $search_condition); + $this->query->add_groupby("$search_index.sid"); + $matches = $this->search_query->matches(); + $placeholder = $this->placeholder(); + $this->query->add_having_expression($this->options['group'], "COUNT(*) >= $placeholder", array($placeholder => $matches)); + } + // Set to NULL to prevent PDO exception when views object is cached. + $this->search_query = NULL; + } + +} diff --git a/core/modules/views/lib/Views/search/Plugin/views/row/View.php b/core/modules/views/lib/Views/search/Plugin/views/row/View.php new file mode 100644 index 0000000000000000000000000000000000000000..fe3f6839fdd30a7e9f92818b886c727cbd426e0f --- /dev/null +++ b/core/modules/views/lib/Views/search/Plugin/views/row/View.php @@ -0,0 +1,54 @@ + TRUE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['score'] = array( + '#type' => 'checkbox', + '#title' => t('Display score'), + '#default_value' => $this->options['score'], + ); + } + + /** + * Override the behavior of the render() function. + */ + function render($row) { + return theme($this->themeFunctions(), + array( + 'view' => $this->view, + 'options' => $this->options, + 'row' => $row + )); + } + +} diff --git a/core/modules/views/lib/Views/search/Plugin/views/sort/Score.php b/core/modules/views/lib/Views/search/Plugin/views/sort/Score.php new file mode 100644 index 0000000000000000000000000000000000000000..152dec4ed3d82215c973dca7732264af93f6d6af --- /dev/null +++ b/core/modules/views/lib/Views/search/Plugin/views/sort/Score.php @@ -0,0 +1,44 @@ +search_score -- and we also + // need to check its relationship to make sure that we're using the same + // one or obviously this won't work. + foreach (array('filter', 'argument') as $type) { + foreach ($this->view->{$type} as $handler) { + if (isset($handler->search_score) && $handler->relationship == $this->relationship) { + $this->query->add_orderby(NULL, NULL, $this->options['order'], $handler->search_score); + $this->tableAlias = $handler->tableAlias; + return; + } + } + } + + // Do absolutely nothing if there is no filter/argument in place; there is no reason to + // sort on the raw scores with this handler. + } + +} diff --git a/core/modules/views/lib/Views/search/ViewsSearchQuery.php b/core/modules/views/lib/Views/search/ViewsSearchQuery.php new file mode 100644 index 0000000000000000000000000000000000000000..5b3508c59b70664c5c3614acc0ae10b6280f9383 --- /dev/null +++ b/core/modules/views/lib/Views/search/ViewsSearchQuery.php @@ -0,0 +1,84 @@ +conditions; + } + + /** + * Returns the words property. + * + * @return array + */ + public function words() { + return $this->words; + } + + /** + * Returns the simple property. + * + * @return bool + */ + public function simple() { + return $this->simple; + } + + /** + * Returns the matches property. + * + * @return int + */ + public function matches() { + return $this->matches; + } + + /** + * Executes and returns the protected parseSearchExpression method. + */ + public function publicParseSearchExpression() { + return $this->parseSearchExpression(); + } + + /** + * Replaces the original condition with a custom one from views recursively. + * + * @param string $search + * The searched value. + * @param string $replace + * The value which replaces the search value. + * @param Drupal\Core\Database\Query\Condition $condition + * The query condition in which the string is replaced. + */ + function condition_replace_string($search, $replace, &$condition) { + if ($condition['field'] instanceof DatabaseCondition) { + $conditions =& $condition['field']->conditions(); + foreach ($conditions as $key => &$subcondition) { + if (is_numeric($key)) { + // As conditions can have subconditions, for example db_or(), the + // function has to be called recursively. + $this->condition_replace_string($search, $replace, $subcondition); + } + } + } + else { + $condition['field'] = str_replace($search, $replace, $condition['field']); + } + } + +} diff --git a/core/modules/views/lib/Views/statistics/Plugin/views/field/AccesslogPath.php b/core/modules/views/lib/Views/statistics/Plugin/views/field/AccesslogPath.php new file mode 100644 index 0000000000000000000000000000000000000000..4b16e204cbb7ab60dfbef884cfe47391a673be85 --- /dev/null +++ b/core/modules/views/lib/Views/statistics/Plugin/views/field/AccesslogPath.php @@ -0,0 +1,71 @@ +options['display_as_link'])) { + $this->additional_fields['path'] = 'path'; + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['display_as_link'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + /** + * Provide link to the page being visited. + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['display_as_link'] = array( + '#title' => t('Display as link'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['display_as_link']), + ); + parent::buildOptionsForm($form, $form_state); + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + + function render_link($data, $values) { + if (!empty($this->options['display_as_link'])) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = $this->get_value($values, 'path'); + $this->options['alter']['html'] = TRUE; + } + + return $data; + } + +} diff --git a/core/modules/views/lib/Views/system/Plugin/views/row/Entity.php b/core/modules/views/lib/Views/system/Plugin/views/row/Entity.php new file mode 100644 index 0000000000000000000000000000000000000000..cec930f0a562f887915ce925b017cbd01b9218c7 --- /dev/null +++ b/core/modules/views/lib/Views/system/Plugin/views/row/Entity.php @@ -0,0 +1,141 @@ +entityType = $this->definition['entity_type']; + + $this->entityInfo = entity_get_info($this->entityType); + $this->base_table = $this->entityInfo['base table']; + $this->base_field = $this->entityInfo['entity keys']['id']; + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::defineOptions(). + */ + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['view_mode'] = array('default' => ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $options = $this->buildViewModeOptions(); + $form['view_mode'] = array( + '#type' => 'select', + '#options' => $options, + '#title' => t('View mode'), + '#default_value' => $this->options['view_mode'], + ); + } + + /** + * Return the main options, which are shown in the summary title. + */ + protected function buildViewModeOptions() { + $options = array(); + if (!empty($this->entityInfo['view modes'])) { + foreach ($this->entityInfo['view modes'] as $mode => $settings) { + $options[$mode] = $settings['label']; + } + } + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\PluginBase::summaryTitle(). + */ + public function summaryTitle() { + $options = $this->buildViewModeOptions(); + return check_plain($options[$this->options['view_mode']]); + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::pre_render(). + */ + public function pre_render($result) { + parent::pre_render($result); + + if ($result) { + // Get all entities which will be used to render in rows. + $entities = array(); + foreach ($result as $row) { + $entity = $row->_entity; + $entity->view = $this->view; + $entities[$entity->id()] = $entity; + } + + // Prepare the render arrays for all rows. + $this->build = entity_view_multiple($entities, $this->options['view_mode']); + } + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::render(). + */ + function render($row) { + $entity_id = $row->{$this->field_alias}; + return drupal_render($this->build[$entity_id]); + } +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTid.php new file mode 100644 index 0000000000000000000000000000000000000000..e58b94dab61f90d4c34ce5e73b9a6f3b2580c41a --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTid.php @@ -0,0 +1,61 @@ + FALSE, 'bool' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['set_breadcrumb'] = array( + '#type' => 'checkbox', + '#title' => t("Set the breadcrumb for the term parents"), + '#description' => t('If selected, the breadcrumb trail will include all parent terms, each one linking to this view. Note that this only works if just one term was received.'), + '#default_value' => !empty($this->options['set_breadcrumb']), + ); + } + + function set_breadcrumb(&$breadcrumb) { + if (empty($this->options['set_breadcrumb']) || !is_numeric($this->argument)) { + return; + } + + return views_taxonomy_set_breadcrumb($breadcrumb, $this); + } + + function title_query() { + $titles = array(); + $result = db_select('taxonomy_term_data', 'td') + ->fields('td', array('name')) + ->condition('td.tid', $this->value) + ->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->name); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepth.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepth.php new file mode 100644 index 0000000000000000000000000000000000000000..54591257add66ba153a13822c11fcd98dea5fe16 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepth.php @@ -0,0 +1,161 @@ + 0); + $options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE); + $options['set_breadcrumb'] = array('default' => FALSE, 'bool' => TRUE); + $options['use_taxonomy_term_path'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['depth'] = array( + '#type' => 'weight', + '#title' => t('Depth'), + '#default_value' => $this->options['depth'], + '#description' => t('The depth will match nodes tagged with terms in the hierarchy. For example, if you have the term "fruit" and a child term "apple", with a depth of 1 (or higher) then filtering for the term "fruit" will get nodes that are tagged with "apple" as well as "fruit". If negative, the reverse is true; searching for "apple" will also pick up nodes tagged with "fruit" if depth is -1 (or lower).'), + ); + + $form['break_phrase'] = array( + '#type' => 'checkbox', + '#title' => t('Allow multiple values'), + '#description' => t('If selected, users can enter multiple values in the form of 1+2+3. Due to the number of JOINs it would require, AND will be treated as OR with this filter.'), + '#default_value' => !empty($this->options['break_phrase']), + ); + + $form['set_breadcrumb'] = array( + '#type' => 'checkbox', + '#title' => t("Set the breadcrumb for the term parents"), + '#description' => t('If selected, the breadcrumb trail will include all parent terms, each one linking to this view. Note that this only works if just one term was received.'), + '#default_value' => !empty($this->options['set_breadcrumb']), + ); + + $form['use_taxonomy_term_path'] = array( + '#type' => 'checkbox', + '#title' => t("Use Drupal's taxonomy term path to create breadcrumb links"), + '#description' => t('If selected, the links in the breadcrumb trail will be created using the standard drupal method instead of the custom views method. This is useful if you are using modules like taxonomy redirect to modify your taxonomy term links.'), + '#default_value' => !empty($this->options['use_taxonomy_term_path']), + '#states' => array( + 'visible' => array( + ':input[name="options[set_breadcrumb]"]' => array('checked' => TRUE), + ), + ), + ); + parent::buildOptionsForm($form, $form_state); + } + + function set_breadcrumb(&$breadcrumb) { + if (empty($this->options['set_breadcrumb']) || !is_numeric($this->argument)) { + return; + } + + return views_taxonomy_set_breadcrumb($breadcrumb, $this); + } + + /** + * Override default_actions() to remove summary actions. + */ + function default_actions($which = NULL) { + if ($which) { + if (in_array($which, array('ignore', 'not found', 'empty', 'default'))) { + return parent::default_actions($which); + } + return; + } + $actions = parent::default_actions(); + unset($actions['summary asc']); + unset($actions['summary desc']); + unset($actions['summary asc by count']); + unset($actions['summary desc by count']); + return $actions; + } + + public function query($group_by = FALSE) { + $this->ensureMyTable(); + + if (!empty($this->options['break_phrase'])) { + $tids = new \stdClass(); + $tids->value = $this->argument; + $tids = $this->breakPhrase($this->argument, $tids); + if ($tids->value == array(-1)) { + return FALSE; + } + + if (count($tids->value) > 1) { + $operator = 'IN'; + } + else { + $operator = '='; + } + + $tids = $tids->value; + } + else { + $operator = "="; + $tids = $this->argument; + } + // Now build the subqueries. + $subquery = db_select('taxonomy_index', 'tn'); + $subquery->addField('tn', 'nid'); + $where = db_or()->condition('tn.tid', $tids, $operator); + $last = "tn"; + + if ($this->options['depth'] > 0) { + $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid"); + $last = "th"; + foreach (range(1, abs($this->options['depth'])) as $count) { + $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid"); + $where->condition("th$count.tid", $tids, $operator); + $last = "th$count"; + } + } + elseif ($this->options['depth'] < 0) { + foreach (range(1, abs($this->options['depth'])) as $count) { + $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent"); + $where->condition("th$count.tid", $tids, $operator); + $last = "th$count"; + } + } + + $subquery->condition($where); + $this->query->add_where(0, "$this->tableAlias.$this->realField", $subquery, 'IN'); + } + + function title() { + $term = taxonomy_term_load($this->argument); + if (!empty($term)) { + return check_plain($term->name); + } + // TODO review text + return t('No name'); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepthModifier.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepthModifier.php new file mode 100644 index 0000000000000000000000000000000000000000..a10fed4215afc7e863560bbbf65b428c42cbb0db --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepthModifier.php @@ -0,0 +1,78 @@ +view->args[$this->position]) ? $this->view->args[$this->position] : NULL; + if (!is_numeric($argument)) { + return; + } + + if ($argument > 10) { + $argument = 10; + } + + if ($argument < -10) { + $argument = -10; + } + + // figure out which argument preceded us. + $keys = array_reverse(array_keys($this->view->argument)); + $skip = TRUE; + foreach ($keys as $key) { + if ($key == $this->options['id']) { + $skip = FALSE; + continue; + } + + if ($skip) { + continue; + } + + if (empty($this->view->argument[$key])) { + continue; + } + + if (isset($handler)) { + unset($handler); + } + + $handler = &$this->view->argument[$key]; + if (empty($handler->definition['accept depth modifier'])) { + continue; + } + + // Finally! + $handler->options['depth'] = $argument; + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/Taxonomy.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/Taxonomy.php new file mode 100644 index 0000000000000000000000000000000000000000..a8c84f387abc2fcd09b253d94fae8206f036061b --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/Taxonomy.php @@ -0,0 +1,40 @@ +argument) { + $term = taxonomy_term_load($this->argument); + if (!empty($term)) { + return check_plain($term->name); + } + } + // TODO review text + return t('No name'); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyMachineName.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyMachineName.php new file mode 100644 index 0000000000000000000000000000000000000000..71e7227a06bbf9e90bf8368059ff07488db9039b --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyMachineName.php @@ -0,0 +1,41 @@ +addField('v', 'name'); + $query->condition('v.machine_name', $this->argument); + $title = $query->execute()->fetchField(); + + if (empty($title)) { + return t('No vocabulary'); + } + + return check_plain($title); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyVid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyVid.php new file mode 100644 index 0000000000000000000000000000000000000000..7613f06257aaa37b6ef5df533537b57aba117442 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyVid.php @@ -0,0 +1,40 @@ +addField('v', 'name'); + $query->condition('v.vid', $this->argument); + $title = $query->execute()->fetchField(); + if (empty($title)) { + return t('No vocabulary'); + } + + return check_plain($title); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_default/Tid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_default/Tid.php new file mode 100644 index 0000000000000000000000000000000000000000..583a0303e332586b68c29e0c707a374ea4fc61e7 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_default/Tid.php @@ -0,0 +1,170 @@ +options['vids'])) { + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($this->options['vids'] as $vid) { + if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) { + $this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name; + } + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['term_page'] = array('default' => TRUE, 'bool' => TRUE); + $options['node'] = array('default' => FALSE, 'bool' => TRUE); + $options['anyall'] = array('default' => ','); + $options['limit'] = array('default' => FALSE, 'bool' => TRUE); + $options['vocabularies'] = array('default' => array()); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['term_page'] = array( + '#type' => 'checkbox', + '#title' => t('Load default filter from term page'), + '#default_value' => $this->options['term_page'], + ); + $form['node'] = array( + '#type' => 'checkbox', + '#title' => t('Load default filter from node page, that\'s good for related taxonomy blocks'), + '#default_value' => $this->options['node'], + ); + + $form['limit'] = array( + '#type' => 'checkbox', + '#title' => t('Limit terms by vocabulary'), + '#default_value' => $this->options['limit'], + '#states' => array( + 'visible' => array( + ':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE), + ), + ), + ); + + $options = array(); + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($vocabularies as $voc) { + $options[$voc->machine_name] = check_plain($voc->name); + } + + $form['vocabularies'] = array( + '#type' => 'checkboxes', + '#title' => t('Vocabularies'), + '#options' => $options, + '#default_value' => $this->options['vocabularies'], + '#states' => array( + 'visible' => array( + ':input[name="options[argument_default][taxonomy_tid][limit]"]' => array('checked' => TRUE), + ':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE), + ), + ), + ); + + $form['anyall'] = array( + '#type' => 'radios', + '#title' => t('Multiple-value handling'), + '#default_value' => $this->options['anyall'], + '#options' => array( + ',' => t('Filter to items that share all terms'), + '+' => t('Filter to items that share any term'), + ), + '#states' => array( + 'visible' => array( + ':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE), + ), + ), + ); + } + + public function submitOptionsForm(&$form, &$form_state, &$options = array()) { + // Filter unselected items so we don't unnecessarily store giant arrays. + $options['vocabularies'] = array_filter($options['vocabularies']); + } + + function get_argument() { + // Load default argument from taxonomy page. + if (!empty($this->options['term_page'])) { + if (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2))) { + return arg(2); + } + } + // Load default argument from node. + if (!empty($this->options['node'])) { + foreach (range(1, 3) as $i) { + $node = menu_get_object('node', $i); + if (!empty($node)) { + break; + } + } + // Just check, if a node could be detected. + if ($node) { + $taxonomy = array(); + $fields = field_info_instances('node', $node->type); + foreach ($fields as $name => $info) { + $field_info = field_info_field($name); + if ($field_info['type'] == 'taxonomy_term_reference') { + $items = field_get_items('node', $node, $name); + if (is_array($items)) { + foreach ($items as $item) { + $taxonomy[$item['tid']] = $field_info['settings']['allowed_values'][0]['vocabulary']; + } + } + } + } + if (!empty($this->options['limit'])) { + $tids = array(); + // filter by vocabulary + foreach ($taxonomy as $tid => $vocab) { + if (!empty($this->options['vocabularies'][$vocab])) { + $tids[] = $tid; + } + } + return implode($this->options['anyall'], $tids); + } + // Return all tids. + else { + return implode($this->options['anyall'], array_keys($taxonomy)); + } + } + } + + // If the current page is a view that takes tid as an argument, + // find the tid argument and return it. + $views_page = views_get_page_view(); + if ($views_page && isset($views_page->argument['tid'])) { + return $views_page->argument['tid']->argument; + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_validator/Term.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_validator/Term.php new file mode 100644 index 0000000000000000000000000000000000000000..a4e250cf162ca97a447f1d644297fd335a22b048 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/argument_validator/Term.php @@ -0,0 +1,202 @@ +options['vids'])) { + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($this->options['vids'] as $vid) { + if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) { + $this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name; + } + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['vocabularies'] = array('default' => array()); + $options['type'] = array('default' => 'tid'); + $options['transform'] = array('default' => FALSE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $vocabularies = taxonomy_vocabulary_get_names(); + $options = array(); + foreach ($vocabularies as $voc) { + $options[$voc->machine_name] = check_plain($voc->name); + } + + $form['vocabularies'] = array( + '#type' => 'checkboxes', + '#prefix' => '', + '#suffix' => '', + '#title' => t('Vocabularies'), + '#options' => $options, + '#default_value' => $this->options['vocabularies'], + '#description' => t('If you wish to validate for specific vocabularies, check them; if none are checked, all terms will pass.'), + ); + + $form['type'] = array( + '#type' => 'select', + '#title' => t('Filter value type'), + '#options' => array( + 'tid' => t('Term ID'), + 'tids' => t('Term IDs separated by , or +'), + 'name' => t('Term name'), + 'convert' => t('Term name converted to Term ID'), + ), + '#default_value' => $this->options['type'], + '#description' => t('Select the form of this filter value; if using term name, it is generally more efficient to convert it to a term ID and use Taxonomy: Term ID rather than Taxonomy: Term Name" as the filter.'), + ); + + $form['transform'] = array( + '#type' => 'checkbox', + '#title' => t('Transform dashes in URL to spaces in term name filter values'), + '#default_value' => $this->options['transform'], + ); + } + + public function submitOptionsForm(&$form, &$form_state, &$options = array()) { + // Filter unselected items so we don't unnecessarily store giant arrays. + $options['vocabularies'] = array_filter($options['vocabularies']); + } + + function validate_argument($argument) { + $vocabularies = array_filter($this->options['vocabularies']); + $type = $this->options['type']; + $transform = $this->options['transform']; + + switch ($type) { + case 'tid': + if (!is_numeric($argument)) { + return FALSE; + } + // @todo Deal with missing addTag('term access') that was removed when + // the db_select that was replaced by the entity_load. + $term = entity_load('taxonomy_term', $argument); + if (!$term) { + return FALSE; + } + $this->argument->validated_title = check_plain($term->name); + return empty($vocabularies) || !empty($vocabularies[$term->vocabulary_machine_name]); + + case 'tids': + // An empty argument is not a term so doesn't pass. + if (empty($argument)) { + return FALSE; + } + + $tids = new stdClass(); + $tids->value = $argument; + $tids = $this->breakPhrase($argument, $tids); + if ($tids->value == array(-1)) { + return FALSE; + } + + $test = drupal_map_assoc($tids->value); + $titles = array(); + + // check, if some tids already verified + static $validated_cache = array(); + foreach ($test as $tid) { + if (isset($validated_cache[$tid])) { + if ($validated_cache[$tid] === FALSE) { + return FALSE; + } + else { + $titles[] = $validated_cache[$tid]; + unset($test[$tid]); + } + } + } + + // if unverified tids left - verify them and cache results + if (count($test)) { + $result = entity_load_multiple('taxonomy_term', $test); + foreach ($result as $term) { + if ($vocabularies && empty($vocabularies[$term->vocabulary_machine_name])) { + $validated_cache[$term->id()] = FALSE; + return FALSE; + } + + $titles[] = $validated_cache[$term->id()] = check_plain($term->name); + unset($test[$term->id()]); + } + } + + // Remove duplicate titles + $titles = array_unique($titles); + + $this->argument->validated_title = implode($tids->operator == 'or' ? ' + ' : ', ', $titles); + // If this is not empty, we did not find a tid. + return empty($test); + + case 'name': + case 'convert': + $terms = entity_load_multiple_by_properties('taxonomy_term', array('name' => $argument)); + $term = reset($terms); + if ($transform) { + $term->name = str_replace(' ', '-', $term->name); + } + + if ($term && (empty($vocabularies) || !empty($vocabularies[$term->vocabulary_machine_name]))) { + if ($type == 'convert') { + $this->argument->argument = $term->id(); + } + $this->argument->validated_title = check_plain($term->name); + return TRUE; + } + return FALSE; + } + } + + function process_summary_arguments(&$args) { + $type = $this->options['type']; + $transform = $this->options['transform']; + $vocabularies = array_filter($this->options['vocabularies']); + + if ($type == 'convert') { + $arg_keys = array_flip($args); + + $result = entity_load_multiple('taxonomy_term', $args); + + if ($transform) { + foreach ($result as $term) { + $term->name = str_replace(' ', '-', $term->name); + } + } + + foreach ($result as $tid => $term) { + $args[$arg_keys[$tid]] = $term; + } + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Language.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..d5ec05f09582c69fbce9493b992443ea6c383e4b --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Language.php @@ -0,0 +1,32 @@ +get_value($values); + $language = language_load($value); + $value = $language ? $language->name : ''; + + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/field/LinkEdit.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/LinkEdit.php new file mode 100644 index 0000000000000000000000000000000000000000..8a0854a9d551fe7dc4345de6f4f57050e4d61568 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/LinkEdit.php @@ -0,0 +1,79 @@ +additional_fields['tid'] = 'tid'; + $this->additional_fields['vid'] = 'vid'; + $this->additional_fields['vocabulary_machine_name'] = array( + 'table' => 'taxonomy_vocabulary', + 'field' => 'machine_name', + ); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['text'] = array('default' => '', 'translatable' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + parent::buildOptionsForm($form, $form_state); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + function render($values) { + // Check there is an actual value, as on a relationship there may not be. + if ($tid = $this->get_value($values, 'tid')) { + // Mock a term object for taxonomy_term_access(). Use machine name and + // vid to ensure compatibility with vid based and machine name based + // access checks. See http://drupal.org/node/995156 + $term = entity_create('taxonomy_term', array( + 'vid' => $values->{$this->aliases['vid']}, + 'vocabulary_machine_name' => $values->{$this->aliases['vocabulary_machine_name']}, + )); + if (taxonomy_term_access('edit', $term)) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + return l($text, 'taxonomy/term/'. $tid . '/edit', array('query' => drupal_get_destination())); + } + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Taxonomy.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Taxonomy.php new file mode 100644 index 0000000000000000000000000000000000000000..90e73ad62ab37070d4170824c50463ff7ca0e0a7 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/Taxonomy.php @@ -0,0 +1,102 @@ +additional_fields['vid'] = 'vid'; + $this->additional_fields['tid'] = 'tid'; + $this->additional_fields['vocabulary_machine_name'] = array( + 'table' => 'taxonomy_vocabulary', + 'field' => 'machine_name', + ); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_taxonomy'] = array('default' => FALSE, 'bool' => TRUE); + $options['convert_spaces'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to taxonomy option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_taxonomy'] = array( + '#title' => t('Link this field to its taxonomy term page'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_taxonomy']), + ); + $form['convert_spaces'] = array( + '#title' => t('Convert spaces in term names to hyphens'), + '#description' => t('This allows links to work with Views taxonomy term arguments.'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['convert_spaces']), + ); + parent::buildOptionsForm($form, $form_state); + } + + /** + * Render whatever the data is as a link to the taxonomy. + * + * Data should be made XSS safe prior to calling this function. + */ + function render_link($data, $values) { + $tid = $this->get_value($values, 'tid'); + if (!empty($this->options['link_to_taxonomy']) && !empty($tid) && $data !== NULL && $data !== '') { + $term = entity_create('taxonomy_term', array( + 'tid' => $tid, + 'vid' => $this->get_value($values, 'vid'), + 'vocabulary_machine_name' => $values->{$this->aliases['vocabulary_machine_name']}, + )); + $this->options['alter']['make_link'] = TRUE; + $uri = $term->uri(); + $this->options['alter']['path'] = $uri['path']; + } + + if (!empty($this->options['convert_spaces'])) { + $data = str_replace(' ', '-', $data); + } + + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/field/TaxonomyIndexTid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/TaxonomyIndexTid.php new file mode 100644 index 0000000000000000000000000000000000000000..8da53dde52e89d9ea26f4fed6b67d98f71391f87 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/field/TaxonomyIndexTid.php @@ -0,0 +1,161 @@ +base_table and no if here? + if ($view->storage->base_table == 'node_revision') { + $this->additional_fields['nid'] = array('table' => 'node_revision', 'field' => 'nid'); + } + else { + $this->additional_fields['nid'] = array('table' => 'node', 'field' => 'nid'); + } + + // Convert legacy vids option to machine name vocabularies. + if (!empty($this->options['vids'])) { + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($this->options['vids'] as $vid) { + if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) { + $this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name; + } + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['link_to_taxonomy'] = array('default' => TRUE, 'bool' => TRUE); + $options['limit'] = array('default' => FALSE, 'bool' => TRUE); + $options['vocabularies'] = array('default' => array()); + + return $options; + } + + /** + * Provide "link to term" option. + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_taxonomy'] = array( + '#title' => t('Link this field to its term page'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['link_to_taxonomy']), + ); + + $form['limit'] = array( + '#type' => 'checkbox', + '#title' => t('Limit terms by vocabulary'), + '#default_value' => $this->options['limit'], + ); + + $options = array(); + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($vocabularies as $voc) { + $options[$voc->machine_name] = check_plain($voc->name); + } + + $form['vocabularies'] = array( + '#type' => 'checkboxes', + '#title' => t('Vocabularies'), + '#options' => $options, + '#default_value' => $this->options['vocabularies'], + '#states' => array( + 'visible' => array( + ':input[name="options[limit]"]' => array('checked' => TRUE), + ), + ), + + ); + + parent::buildOptionsForm($form, $form_state); + } + + /** + * Add this term to the query + */ + public function query() { + $this->add_additional_fields(); + } + + function pre_render(&$values) { + $this->field_alias = $this->aliases['nid']; + $nids = array(); + foreach ($values as $result) { + if (!empty($result->{$this->aliases['nid']})) { + $nids[] = $result->{$this->aliases['nid']}; + } + } + + if ($nids) { + $query = db_select('taxonomy_term_data', 'td'); + $query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid'); + $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); + $query->fields('td'); + $query->addField('tn', 'nid', 'node_nid'); + $query->addField('tv', 'name', 'vocabulary'); + $query->addField('tv', 'machine_name', 'vocabulary_machine_name'); + $query->orderby('td.weight'); + $query->orderby('td.name'); + $query->condition('tn.nid', $nids); + $query->addTag('term_access'); + $vocabs = array_filter($this->options['vocabularies']); + if (!empty($this->options['limit']) && !empty($vocabs)) { + $query->condition('tv.machine_name', $vocabs); + } + $result = $query->execute(); + + foreach ($result as $term) { + $this->items[$term->node_nid][$term->tid]['name'] = check_plain($term->name); + $this->items[$term->node_nid][$term->tid]['tid'] = $term->tid; + $this->items[$term->node_nid][$term->tid]['vocabulary_machine_name'] = check_plain($term->vocabulary_machine_name); + $this->items[$term->node_nid][$term->tid]['vocabulary'] = check_plain($term->vocabulary); + + if (!empty($this->options['link_to_taxonomy'])) { + $this->items[$term->node_nid][$term->tid]['make_link'] = TRUE; + $this->items[$term->node_nid][$term->tid]['path'] = 'taxonomy/term/' . $term->tid; + } + } + } + } + + function render_item($count, $item) { + return $item['name']; + } + + function document_self_tokens(&$tokens) { + $tokens['[' . $this->options['id'] . '-tid' . ']'] = t('The taxonomy term ID for the term.'); + $tokens['[' . $this->options['id'] . '-name' . ']'] = t('The taxonomy term name for the term.'); + $tokens['[' . $this->options['id'] . '-vocabulary-machine-name' . ']'] = t('The machine name for the vocabulary the term belongs to.'); + $tokens['[' . $this->options['id'] . '-vocabulary' . ']'] = t('The name for the vocabulary the term belongs to.'); + } + + function add_self_tokens(&$tokens, $item) { + foreach (array('tid', 'name', 'vocabulary_machine_name', 'vocabulary') as $token) { + // Replace _ with - for the vocabulary machine name. + $tokens['[' . $this->options['id'] . '-' . str_replace('_', '-', $token) . ']'] = isset($item[$token]) ? $item[$token] : ''; + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTid.php new file mode 100644 index 0000000000000000000000000000000000000000..f4300b082561826f9c58e14571e0c507ce0700e4 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTid.php @@ -0,0 +1,376 @@ +definition['vocabulary'])) { + $this->options['vocabulary'] = $this->definition['vocabulary']; + } + + // Convert legacy vid option to machine name vocabulary. + if (isset($this->options['vid']) && !empty($this->options['vid']) & empty($this->options['vocabulary'])) { + $vocabularies = taxonomy_vocabulary_get_names(); + $vid = $this->options['vid']; + if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) { + $this->options['vocabulary'] = $vocabularies[$vid]->machine_name; + } + } + } + + public function hasExtraOptions() { return TRUE; } + + function get_value_options() { /* don't overwrite the value options */ } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['type'] = array('default' => 'textfield'); + $options['limit'] = array('default' => TRUE, 'bool' => TRUE); + $options['vocabulary'] = array('default' => 0); + $options['hierarchy'] = array('default' => 0); + $options['error_message'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + public function buildExtraOptionsForm(&$form, &$form_state) { + $vocabularies = taxonomy_vocabulary_get_names(); + $options = array(); + foreach ($vocabularies as $voc) { + $options[$voc->machine_name] = check_plain($voc->name); + } + + if ($this->options['limit']) { + // We only do this when the form is displayed. + if (empty($this->options['vocabulary'])) { + $first_vocabulary = reset($vocabularies); + $this->options['vocabulary'] = $first_vocabulary->machine_name; + } + + if (empty($this->definition['vocabulary'])) { + $form['vocabulary'] = array( + '#type' => 'radios', + '#title' => t('Vocabulary'), + '#options' => $options, + '#description' => t('Select which vocabulary to show terms for in the regular options.'), + '#default_value' => $this->options['vocabulary'], + ); + } + } + + $form['type'] = array( + '#type' => 'radios', + '#title' => t('Selection type'), + '#options' => array('select' => t('Dropdown'), 'textfield' => t('Autocomplete')), + '#default_value' => $this->options['type'], + ); + + $form['hierarchy'] = array( + '#type' => 'checkbox', + '#title' => t('Show hierarchy in dropdown'), + '#default_value' => !empty($this->options['hierarchy']), + '#states' => array( + 'visible' => array( + ':input[name="options[type]"]' => array('value' => 'select'), + ), + ), + ); + } + + function value_form(&$form, &$form_state) { + $vocabulary = taxonomy_vocabulary_machine_name_load($this->options['vocabulary']); + if (empty($vocabulary) && $this->options['limit']) { + $form['markup'] = array( + '#markup' => '' . t('An invalid vocabulary is selected. Please change it in the options.') . '', + ); + return; + } + + if ($this->options['type'] == 'textfield') { + $default = ''; + if ($this->value) { + $result = db_select('taxonomy_term_data', 'td') + ->fields('td') + ->condition('td.tid', $this->value) + ->execute(); + foreach ($result as $term) { + if ($default) { + $default .= ', '; + } + $default .= $term->name; + } + } + + $form['value'] = array( + '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'), + '#type' => 'textfield', + '#default_value' => $default, + ); + + if ($this->options['limit']) { + $form['value']['#autocomplete_path'] = 'admin/views/ajax/autocomplete/taxonomy/' . $vocabulary->vid; + } + } + else { + if (!empty($this->options['hierarchy']) && $this->options['limit']) { + $tree = taxonomy_get_tree($vocabulary->vid); + $options = array(); + + if ($tree) { + foreach ($tree as $term) { + $choice = new stdClass(); + $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name); + $options[] = $choice; + } + } + } + else { + $options = array(); + $query = db_select('taxonomy_term_data', 'td'); + $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); + $query->fields('td'); + $query->orderby('tv.weight'); + $query->orderby('tv.name'); + $query->orderby('td.weight'); + $query->orderby('td.name'); + $query->addTag('term_access'); + if ($this->options['limit']) { + $query->condition('tv.machine_name', $vocabulary->machine_name); + } + $result = $query->execute(); + foreach ($result as $term) { + $options[$term->tid] = $term->name; + } + } + + $default_value = (array) $this->value; + + if (!empty($form_state['exposed'])) { + $identifier = $this->options['expose']['identifier']; + + if (!empty($this->options['expose']['reduce'])) { + $options = $this->reduce_value_options($options); + + if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) { + $default_value = array(); + } + } + + if (empty($this->options['expose']['multiple'])) { + if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) { + $default_value = 'All'; + } + elseif (empty($default_value)) { + $keys = array_keys($options); + $default_value = array_shift($keys); + } + // Due to #1464174 there is a chance that array('') was saved in the admin ui. + // Let's choose a safe default value. + elseif ($default_value == array('')) { + $default_value = 'All'; + } + else { + $copy = $default_value; + $default_value = array_shift($copy); + } + } + } + $form['value'] = array( + '#type' => 'select', + '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'), + '#multiple' => TRUE, + '#options' => $options, + '#size' => min(9, count($options)), + '#default_value' => $default_value, + ); + + if (!empty($form_state['exposed']) && isset($identifier) && !isset($form_state['input'][$identifier])) { + $form_state['input'][$identifier] = $default_value; + } + } + + if (empty($form_state['exposed'])) { + // Retain the helper option + $this->helper->buildOptionsForm($form, $form_state); + } + } + + function value_validate($form, &$form_state) { + // We only validate if they've chosen the text field style. + if ($this->options['type'] != 'textfield') { + return; + } + + $values = drupal_explode_tags($form_state['values']['options']['value']); + $tids = $this->validate_term_strings($form['value'], $values); + + if ($tids) { + $form_state['values']['options']['value'] = $tids; + } + } + + public function acceptExposedInput($input) { + if (empty($this->options['exposed'])) { + return TRUE; + } + + // If view is an attachment and is inheriting exposed filters, then assume + // exposed input has already been validated + if (!empty($this->view->is_attachment) && $this->view->display_handler->usesExposed()) { + $this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']]; + } + + // If it's non-required and there's no value don't bother filtering. + if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) { + return FALSE; + } + + $rc = parent::acceptExposedInput($input); + if ($rc) { + // If we have previously validated input, override. + if (isset($this->validated_exposed_input)) { + $this->value = $this->validated_exposed_input; + } + } + + return $rc; + } + + public function validateExposed(&$form, &$form_state) { + if (empty($this->options['exposed'])) { + return; + } + + $identifier = $this->options['expose']['identifier']; + + // We only validate if they've chosen the text field style. + if ($this->options['type'] != 'textfield') { + if ($form_state['values'][$identifier] != 'All') { + $this->validated_exposed_input = (array) $form_state['values'][$identifier]; + } + return; + } + + if (empty($this->options['expose']['identifier'])) { + return; + } + + $values = drupal_explode_tags($form_state['values'][$identifier]); + + $tids = $this->validate_term_strings($form[$identifier], $values); + if ($tids) { + $this->validated_exposed_input = $tids; + } + } + + /** + * Validate the user string. Since this can come from either the form + * or the exposed filter, this is abstracted out a bit so it can + * handle the multiple input sources. + * + * @param $form + * The form which is used, either the views ui or the exposed filters. + * @param $values + * The taxonomy names which will be converted to tids. + * + * @return array + * The taxonomy ids fo all validated terms. + */ + function validate_term_strings(&$form, $values) { + if (empty($values)) { + return array(); + } + + $tids = array(); + $names = array(); + $missing = array(); + foreach ($values as $value) { + $missing[strtolower($value)] = TRUE; + $names[] = $value; + } + + if (!$names) { + return FALSE; + } + + $query = db_select('taxonomy_term_data', 'td'); + $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); + $query->fields('td'); + $query->condition('td.name', $names); + $query->condition('tv.machine_name', $this->options['vocabulary']); + $query->addTag('term_access'); + $result = $query->execute(); + foreach ($result as $term) { + unset($missing[strtolower($term->name)]); + $tids[] = $term->tid; + } + + if ($missing && !empty($this->options['error_message'])) { + form_error($form, format_plural(count($missing), 'Unable to find term: @terms', 'Unable to find terms: @terms', array('@terms' => implode(', ', array_keys($missing))))); + } + elseif ($missing && empty($this->options['error_message'])) { + $tids = array(0); + } + + return $tids; + } + + function value_submit($form, &$form_state) { + // prevent array_filter from messing up our arrays in parent submit. + } + + public function buildExposeForm(&$form, &$form_state) { + parent::buildExposeForm($form, $form_state); + if ($this->options['type'] != 'select') { + unset($form['expose']['reduce']); + } + $form['error_message'] = array( + '#type' => 'checkbox', + '#title' => t('Display error message'), + '#default_value' => !empty($this->options['error_message']), + ); + } + + public function adminSummary() { + // set up $this->value_options for the parent summary + $this->value_options = array(); + + if ($this->value) { + $this->value = array_filter($this->value); + $result = db_select('taxonomy_term_data', 'td') + ->fields('td') + ->condition('td.tid', $this->value) + ->execute(); + foreach ($result as $term) { + $this->value_options[$term->tid] = $term->name; + } + } + return parent::adminSummary(); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTidDepth.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTidDepth.php new file mode 100644 index 0000000000000000000000000000000000000000..53100068be76db3a67840a1cc3970f33ca047170 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTidDepth.php @@ -0,0 +1,111 @@ + t('Is one of'), + ); + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['depth'] = array('default' => 0); + + return $options; + } + + public function buildExtraOptionsForm(&$form, &$form_state) { + parent::buildExtraOptionsForm($form, $form_state); + + $form['depth'] = array( + '#type' => 'weight', + '#title' => t('Depth'), + '#default_value' => $this->options['depth'], + '#description' => t('The depth will match nodes tagged with terms in the hierarchy. For example, if you have the term "fruit" and a child term "apple", with a depth of 1 (or higher) then filtering for the term "fruit" will get nodes that are tagged with "apple" as well as "fruit". If negative, the reverse is true; searching for "apple" will also pick up nodes tagged with "fruit" if depth is -1 (or lower).'), + ); + } + + public function query() { + // If no filter values are present, then do nothing. + if (count($this->value) == 0) { + return; + } + elseif (count($this->value) == 1) { + // Somethis $this->value is an array with a single element so convert it. + if (is_array($this->value)) { + $this->value = current($this->value); + } + $operator = '='; + } + else { + $operator = 'IN';# " IN (" . implode(', ', array_fill(0, sizeof($this->value), '%d')) . ")"; + } + + // The normal use of ensureMyTable() here breaks Views. + // So instead we trick the filter into using the alias of the base table. + // See http://drupal.org/node/271833 + // If a relationship is set, we must use the alias it provides. + if (!empty($this->relationship)) { + $this->tableAlias = $this->relationship; + } + // If no relationship, then use the alias of the base table. + elseif (isset($this->query->table_queue[$this->query->base_table]['alias'])) { + $this->tableAlias = $this->query->table_queue[$this->query->base_table]['alias']; + } + // This should never happen, but if it does, we fail quietly. + else { + return; + } + + // Now build the subqueries. + $subquery = db_select('taxonomy_index', 'tn'); + $subquery->addField('tn', 'nid'); + $where = db_or()->condition('tn.tid', $this->value, $operator); + $last = "tn"; + + if ($this->options['depth'] > 0) { + $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid"); + $last = "th"; + foreach (range(1, abs($this->options['depth'])) as $count) { + $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid"); + $where->condition("th$count.tid", $this->value, $operator); + $last = "th$count"; + } + } + elseif ($this->options['depth'] < 0) { + foreach (range(1, abs($this->options['depth'])) as $count) { + $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent"); + $where->condition("th$count.tid", $this->value, $operator); + $last = "th$count"; + } + } + + $subquery->condition($where); + $this->query->add_where($this->options['group'], "$this->tableAlias.$this->realField", $subquery, 'IN'); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyMachineName.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyMachineName.php new file mode 100644 index 0000000000000000000000000000000000000000..96ef9ba9aa73d3c66e5e730c00433b8b0b165a18 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyMachineName.php @@ -0,0 +1,37 @@ +value_options)) { + return; + } + + $this->value_options = array(); + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($vocabularies as $voc) { + $this->value_options[$voc->machine_name] = $voc->name; + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyVid.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyVid.php new file mode 100644 index 0000000000000000000000000000000000000000..22b1a0af2ab86fdf3ca211524892c67a4d1f0758 --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyVid.php @@ -0,0 +1,37 @@ +value_options)) { + return; + } + + $this->value_options = array(); + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($vocabularies as $voc) { + $this->value_options[$voc->vid] = $voc->name; + } + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/relationship/NodeTermData.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/relationship/NodeTermData.php new file mode 100644 index 0000000000000000000000000000000000000000..c4feb13c98bb15d1c6d89f18f90d8cffe25fe30f --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/relationship/NodeTermData.php @@ -0,0 +1,105 @@ +options['vids'])) { + $vocabularies = taxonomy_vocabulary_get_names(); + foreach ($this->options['vids'] as $vid) { + if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) { + $this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name; + } + } + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['vocabularies'] = array('default' => array()); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $vocabularies = taxonomy_vocabulary_get_names(); + $options = array(); + foreach ($vocabularies as $voc) { + $options[$voc->machine_name] = check_plain($voc->name); + } + + $form['vocabularies'] = array( + '#type' => 'checkboxes', + '#title' => t('Vocabularies'), + '#options' => $options, + '#default_value' => $this->options['vocabularies'], + '#description' => t('Choose which vocabularies you wish to relate. Remember that every term found will create a new record, so this relationship is best used on just one vocabulary that has only one term per node.'), + ); + parent::buildOptionsForm($form, $form_state); + } + + /** + * Called to implement a relationship in a query. + */ + public function query() { + $this->ensureMyTable(); + + $def = $this->definition; + $def['table'] = 'taxonomy_term_data'; + + if (!array_filter($this->options['vocabularies'])) { + $taxonomy_index = $this->query->add_table('taxonomy_index', $this->relationship); + $def['left_table'] = $taxonomy_index; + $def['left_field'] = 'tid'; + $def['field'] = 'tid'; + $def['type'] = empty($this->options['required']) ? 'LEFT' : 'INNER'; + } + else { + // If vocabularies are supplied join a subselect instead + $def['left_table'] = $this->tableAlias; + $def['left_field'] = 'nid'; + $def['field'] = 'nid'; + $def['type'] = empty($this->options['required']) ? 'LEFT' : 'INNER'; + $def['adjusted'] = TRUE; + + $query = db_select('taxonomy_term_data', 'td'); + $query->addJoin($def['type'], 'taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); + $query->addJoin($def['type'], 'taxonomy_index', 'tn', 'tn.tid = td.tid'); + $query->condition('tv.machine_name', array_filter($this->options['vocabularies'])); + $query->addTag('term_access'); + $query->fields('td'); + $query->fields('tn', array('nid')); + $def['table formula'] = $query; + } + + $join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $def); + + // use a short alias for this: + $alias = $def['table'] . '_' . $this->table; + + $this->alias = $this->query->add_relationship($alias, $join, 'taxonomy_term_data', $this->relationship); + } + +} diff --git a/core/modules/views/lib/Views/taxonomy/Plugin/views/wizard/TaxonomyTerm.php b/core/modules/views/lib/Views/taxonomy/Plugin/views/wizard/TaxonomyTerm.php new file mode 100644 index 0000000000000000000000000000000000000000..465ba46a7eb1fdd6817529801aa019440cac3d9e --- /dev/null +++ b/core/modules/views/lib/Views/taxonomy/Plugin/views/wizard/TaxonomyTerm.php @@ -0,0 +1,72 @@ + 'tid', + 'table' => 'taxonomy_term_data', + 'field' => 'tid', + 'exclude' => TRUE, + 'alter' => array( + 'alter_text' => TRUE, + 'text' => 'taxonomy/term/[tid]' + ) + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: Taxonomy: Term */ + $display_options['fields']['name']['id'] = 'name'; + $display_options['fields']['name']['table'] = 'taxonomy_term_data'; + $display_options['fields']['name']['field'] = 'name'; + $display_options['fields']['name']['label'] = ''; + $display_options['fields']['name']['alter']['alter_text'] = 0; + $display_options['fields']['name']['alter']['make_link'] = 0; + $display_options['fields']['name']['alter']['absolute'] = 0; + $display_options['fields']['name']['alter']['trim'] = 0; + $display_options['fields']['name']['alter']['word_boundary'] = 0; + $display_options['fields']['name']['alter']['ellipsis'] = 0; + $display_options['fields']['name']['alter']['strip_tags'] = 0; + $display_options['fields']['name']['alter']['html'] = 0; + $display_options['fields']['name']['hide_empty'] = 0; + $display_options['fields']['name']['empty_zero'] = 0; + $display_options['fields']['name']['link_to_taxonomy'] = 1; + + return $display_options; + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/argument/NodeTnid.php b/core/modules/views/lib/Views/translation/Plugin/views/argument/NodeTnid.php new file mode 100644 index 0000000000000000000000000000000000000000..53d60fc85c4bdc1667164b32eb98d77ef82cbd9a --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/argument/NodeTnid.php @@ -0,0 +1,41 @@ +addField('n', 'title'); + $query->condition('n.tnid', $this->value); + $result = $query->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->title); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/field/NodeLinkTranslate.php b/core/modules/views/lib/Views/translation/Plugin/views/field/NodeLinkTranslate.php new file mode 100644 index 0000000000000000000000000000000000000000..b915c403ba41805e3a1805d9ebeb90f7d20338d4 --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/field/NodeLinkTranslate.php @@ -0,0 +1,41 @@ +get_value($values); + $node->status = 1; // unpublished nodes ignore access control + if (empty($node->langcode) || !translation_supported_type($node->type) || !node_access('view', $node) || !user_access('translate content')) { + return; + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/$node->nid/translate"; + $this->options['alter']['query'] = drupal_get_destination(); + + $text = !empty($this->options['text']) ? $this->options['text'] : t('translate'); + return $text; + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/field/NodeTranslationLink.php b/core/modules/views/lib/Views/translation/Plugin/views/field/NodeTranslationLink.php new file mode 100644 index 0000000000000000000000000000000000000000..cb5252ba6d192c75d0bce8c1b8ed461d77c0f888 --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/field/NodeTranslationLink.php @@ -0,0 +1,66 @@ +additional_fields['nid'] = 'nid'; + $this->additional_fields['tnid'] = 'tnid'; + $this->additional_fields['title'] = 'title'; + $this->additional_fields['langcode'] = 'langcode'; + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + function render($values) { + $value = $this->get_value($values, 'tnid'); + return $this->render_link($this->sanitizeValue($value), $values); + } + + function render_link($data, $values) { + $language_interface = language(LANGUAGE_TYPE_INTERFACE); + + $tnid = $this->get_value($values, 'tnid'); + // Only load translations if the node isn't in the current language. + if ($this->get_value($values, 'langcode') != $language_interface->langcode) { + $translations = translation_node_get_translations($tnid); + if (isset($translations[$language_interface->langcode])) { + $values->{$this->aliases['nid']} = $translations[$language_interface->langcode]->nid; + $values->{$this->aliases['title']} = $translations[$language_interface->langcode]->title; + } + } + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "node/" . $this->get_value($values, 'nid'); + return $this->get_value($values, 'title'); + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnid.php b/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnid.php new file mode 100644 index 0000000000000000000000000000000000000000..af11db0cf84e5feada0e679a644d22074dec0ed7 --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnid.php @@ -0,0 +1,58 @@ + 'radios', + '#title' => t('Include untranslated content'), + '#default_value' => $this->operator, + '#options' => array( + 1 => t('Yes'), + 0 => t('No'), + ), + ); + } + + public function canExpose() { return FALSE; } + + public function query() { + $table = $this->ensureMyTable(); + // Select for source translations (tnid = nid). Conditionally, also accept either untranslated nodes (tnid = 0). + $this->query->add_where_expression($this->options['group'], "$table.tnid = $table.nid" . ($this->operator ? " OR $table.tnid = 0" : '')); + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnidChild.php b/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnidChild.php new file mode 100644 index 0000000000000000000000000000000000000000..f05606257345a508e923f98837a63508f9c5b67d --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnidChild.php @@ -0,0 +1,36 @@ +ensureMyTable(); + $this->query->add_where_expression($this->options['group'], "$table.tnid <> $table.nid AND $table.tnid > 0"); + } + +} diff --git a/core/modules/views/lib/Views/translation/Plugin/views/relationship/Translation.php b/core/modules/views/lib/Views/translation/Plugin/views/relationship/Translation.php new file mode 100644 index 0000000000000000000000000000000000000000..b4821b3f9661bc9a50579d75ddbfb09bf0731e03 --- /dev/null +++ b/core/modules/views/lib/Views/translation/Plugin/views/relationship/Translation.php @@ -0,0 +1,113 @@ + 'current'); + + return $options; + } + + /** + * Add a translation selector. + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $options = array( + 'all' => t('All'), + 'current' => t('Current language'), + 'default' => t('Default language'), + ); + $options = array_merge($options, views_language_list()); + $form['language'] = array( + '#type' => 'select', + '#options' => $options, + '#default_value' => $this->options['language'], + '#title' => t('Translation option'), + '#description' => t('The translation options allows you to select which translation or translations in a translation set join on. Select "Current language" or "Default language" to join on the translation in the current or default language respectively. Select a specific language to join on a translation in that language. If you select "All", each translation will create a new row, which may appear to cause duplicates.'), + ); + } + + /** + * Called to implement a relationship in a query. + */ + public function query() { + // Figure out what base table this relationship brings to the party. + $table_data = views_fetch_data($this->definition['base']); + $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field']; + + $this->ensureMyTable(); + + $def = $this->definition; + $def['table'] = $this->definition['base']; + $def['field'] = $base_field; + $def['left_table'] = $this->tableAlias; + $def['left_field'] = $this->field; + $def['adjusted'] = TRUE; + if (!empty($this->options['required'])) { + $def['type'] = 'INNER'; + } + + $def['extra'] = array(); + if ($this->options['language'] != 'all') { + switch ($this->options['language']) { + case 'current': + $def['extra'][] = array( + 'field' => 'langcode', + 'value' => '***CURRENT_LANGUAGE***', + ); + break; + case 'default': + $def['extra'][] = array( + 'field' => 'langcode', + 'value' => '***DEFAULT_LANGUAGE***', + ); + break; + // Other values will be the language codes. + default: + $def['extra'][] = array( + 'field' => 'langcode', + 'value' => $this->options['language'], + ); + break; + } + } + + if (!empty($def['join_id'])) { + $id = $def['join_id']; + } + else { + $id = 'standard'; + } + $join = drupal_container()->get('plugin.manager.views.join')->createInstance($id, $def); + + // use a short alias for this: + $alias = $def['table'] . '_' . $this->table; + + $this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/access/Permission.php b/core/modules/views/lib/Views/user/Plugin/views/access/Permission.php new file mode 100644 index 0000000000000000000000000000000000000000..92819e6b7cf11e24b73f7961c8b9d886513f1135 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/access/Permission.php @@ -0,0 +1,81 @@ +options['perm'], $account); + } + + function get_access_callback() { + return array('views_check_perm', array($this->options['perm'])); + } + + public function summaryTitle() { + $permissions = module_invoke_all('permission'); + if (isset($permissions[$this->options['perm']])) { + return $permissions[$this->options['perm']]['title']; + } + + return t($this->options['perm']); + } + + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['perm'] = array('default' => 'access content'); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $perms = array(); + $module_info = system_get_info('module'); + + // Get list of permissions + foreach (module_implements('permission') as $module) { + $permissions = module_invoke($module, 'permission'); + foreach ($permissions as $name => $perm) { + $perms[$module_info[$module]['name']][$name] = strip_tags($perm['title']); + } + } + + ksort($perms); + + $form['perm'] = array( + '#type' => 'select', + '#options' => $perms, + '#title' => t('Permission'), + '#default_value' => $this->options['perm'], + '#description' => t('Only users with the selected permission flag will be able to access this display. Note that users with "access all views" can see any view, regardless of other permissions.'), + ); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/access/Role.php b/core/modules/views/lib/Views/user/Plugin/views/access/Role.php new file mode 100644 index 0000000000000000000000000000000000000000..985a9ddc6b6ba009ea5fb08561d928325e2e5972 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/access/Role.php @@ -0,0 +1,85 @@ +options['role']), $account); + } + + function get_access_callback() { + return array('views_check_roles', array(array_filter($this->options['role']))); + } + + public function summaryTitle() { + $count = count($this->options['role']); + if ($count < 1) { + return t('No role(s) selected'); + } + elseif ($count > 1) { + return t('Multiple roles'); + } + else { + $rids = user_roles(); + $rid = reset($this->options['role']); + return check_plain($rids[$rid]); + } + } + + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['role'] = array('default' => array()); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['role'] = array( + '#type' => 'checkboxes', + '#title' => t('Role'), + '#default_value' => $this->options['role'], + '#options' => array_map('check_plain', $this->getRoles()), + '#description' => t('Only the checked roles will be able to access this display. Note that users with "access all views" can see any view, regardless of role.'), + ); + } + + public function validateOptionsForm(&$form, &$form_state) { + if (!array_filter($form_state['values']['access_options']['role'])) { + form_error($form['role'], t('You must select at least one role if type is "by role"')); + } + } + + public function submitOptionsForm(&$form, &$form_state) { + // I hate checkboxes. + $form_state['values']['access_options']['role'] = array_filter($form_state['values']['access_options']['role']); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/argument/RolesRid.php b/core/modules/views/lib/Views/user/Plugin/views/argument/RolesRid.php new file mode 100644 index 0000000000000000000000000000000000000000..80c7c67408c705fe4fc7c132238bb7aac029024b --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/argument/RolesRid.php @@ -0,0 +1,38 @@ +addField('r', 'name'); + $query->condition('r.rid', $this->value); + $result = $query->execute(); + foreach ($result as $term) { + $titles[] = check_plain($term->name); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/argument/Uid.php b/core/modules/views/lib/Views/user/Plugin/views/argument/Uid.php new file mode 100644 index 0000000000000000000000000000000000000000..655bc9363e1203c46ee12bbf5829e6cdd557506f --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/argument/Uid.php @@ -0,0 +1,45 @@ +argument) { + return array(config('user.settings')->get('anonymous')); + } + + $titles = array(); + + $users = user_load_multiple($this->value); + foreach ($users as $account) { + $titles[] = check_plain($account->label()); + } + return $titles; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/argument_default/CurrentUser.php b/core/modules/views/lib/Views/user/Plugin/views/argument_default/CurrentUser.php new file mode 100644 index 0000000000000000000000000000000000000000..90e4b1a4a0314903079a8db16d4a8e488dcc66b5 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/argument_default/CurrentUser.php @@ -0,0 +1,32 @@ +uid; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/argument_default/User.php b/core/modules/views/lib/Views/user/Plugin/views/argument_default/User.php new file mode 100644 index 0000000000000000000000000000000000000000..c12d6537677c34ef143224e3593ccc0a132b444f --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/argument_default/User.php @@ -0,0 +1,85 @@ + '', 'bool' => TRUE, 'translatable' => FALSE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['user'] = array( + '#type' => 'checkbox', + '#title' => t('Also look for a node and use the node author'), + '#default_value' => $this->options['user'], + ); + } + + function get_argument() { + foreach (range(1, 3) as $i) { + $user = menu_get_object('user', $i); + if (!empty($user)) { + return $user->uid; + } + } + + foreach (range(1, 3) as $i) { + $user = menu_get_object('user_uid_optional', $i); + if (!empty($user)) { + return $user->uid; + } + } + + if (!empty($this->options['user'])) { + foreach (range(1, 3) as $i) { + $node = menu_get_object('node', $i); + if (!empty($node)) { + return $node->uid; + } + } + } + + if (arg(0) == 'user' && is_numeric(arg(1))) { + return arg(1); + } + + if (!empty($this->options['user'])) { + if (arg(0) == 'node' && is_numeric(arg(1))) { + $node = node_load(arg(1)); + if ($node) { + return $node->uid; + } + } + } + + // If the current page is a view that takes uid as an argument, return the uid. + $view = views_get_page_view(); + + if ($view && isset($view->argument['uid'])) { + return $view->argument['uid']->argument; + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/argument_validator/User.php b/core/modules/views/lib/Views/user/Plugin/views/argument_validator/User.php new file mode 100644 index 0000000000000000000000000000000000000000..3ef3ed62d3ab5289d00f404827dd5e45ec42ff30 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/argument_validator/User.php @@ -0,0 +1,152 @@ + 'uid'); + $options['restrict_roles'] = array('default' => FALSE, 'bool' => TRUE); + $options['roles'] = array('default' => array()); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['type'] = array( + '#type' => 'radios', + '#title' => t('Type of user filter value to allow'), + '#options' => array( + 'uid' => t('Only allow numeric UIDs'), + 'name' => t('Only allow string usernames'), + 'either' => t('Allow both numeric UIDs and string usernames'), + ), + '#default_value' => $this->options['type'], + ); + + $form['restrict_roles'] = array( + '#type' => 'checkbox', + '#title' => t('Restrict user based on role'), + '#default_value' => $this->options['restrict_roles'], + ); + + $form['roles'] = array( + '#type' => 'checkboxes', + '#title' => t('Restrict to the selected roles'), + '#options' => array_map('check_plain', user_roles(TRUE)), + '#default_value' => $this->options['roles'], + '#description' => t('If no roles are selected, users from any role will be allowed.'), + '#states' => array( + 'visible' => array( + ':input[name="options[validate][options][user][restrict_roles]"]' => array('checked' => TRUE), + ), + ), + ); + } + + public function submitOptionsForm(&$form, &$form_state, &$options = array()) { + // filter trash out of the options so we don't store giant unnecessary arrays + $options['roles'] = array_filter($options['roles']); + } + + function validate_argument($argument) { + $type = $this->options['type']; + // is_numeric() can return false positives, so we ensure it's an integer. + // However, is_integer() will always fail, since $argument is a string. + if (is_numeric($argument) && $argument == (int)$argument) { + if ($type == 'uid' || $type == 'either') { + if ($argument == $GLOBALS['user']->uid) { + // If you assign an object to a variable in PHP, the variable + // automatically acts as a reference, not a copy, so we use + // clone to ensure that we don't actually mess with the + // real global $user object. + $account = clone $GLOBALS['user']; + } + $condition = 'uid'; + } + } + else { + if ($type == 'name' || $type == 'either') { + $name = !empty($GLOBALS['user']->name) ? $GLOBALS['user']->name : config('user.settings')->get('anonymous'); + if ($argument == $name) { + $account = clone $GLOBALS['user']; + } + $condition = 'name'; + } + } + + // If we don't have a WHERE clause, the argument is invalid. + if (empty($condition)) { + return FALSE; + } + + if (!isset($account)) { + $account = db_select('users', 'u') + ->fields('u', array('uid', 'name')) + ->condition($condition, $argument) + ->execute() + ->fetchObject(); + } + if (empty($account)) { + // User not found. + return FALSE; + } + + // See if we're filtering users based on roles. + if (!empty($this->options['restrict_roles']) && !empty($this->options['roles'])) { + $roles = $this->options['roles']; + $account->roles = array(); + $account->roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID; + $query = db_select('users_roles', 'u'); + $query->addField('u', 'rid'); + $query->condition('u.uid', $account->uid); + $result = $query->execute(); + foreach ($result as $role) { + $account->roles[] = $role->rid; + } + if (!(bool) array_intersect($account->roles, $roles)) { + return FALSE; + } + } + + $this->argument->argument = $account->uid; + $this->argument->validated_title = check_plain(user_format_name($account)); + return TRUE; + } + + function process_summary_arguments(&$args) { + // If the validation says the input is an username, we should reverse the + // argument so it works for example for generation summary urls. + $uids_arg_keys = array_flip($args); + if ($this->options['type'] == 'name') { + $users = user_load_multiple($args); + foreach ($users as $uid => $account) { + $args[$uids_arg_keys[$uid]] = $account->name; + } + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Language.php b/core/modules/views/lib/Views/user/Plugin/views/field/Language.php new file mode 100644 index 0000000000000000000000000000000000000000..0a4c160f363aa57390b2ea0386cc3c9c93dcdc3e --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Language.php @@ -0,0 +1,49 @@ +get_value($values, 'uid'); + if (!empty($this->options['link_to_user'])) { + $uid = $this->get_value($values, 'uid'); + if (user_access('access user profiles') && $uid) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = 'user/' . $uid; + } + } + if (empty($data)) { + $lang = language_default(); + } + else { + $lang = language_list(); + $lang = $lang[$data]; + } + + return $this->sanitizeValue($lang->name); + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Link.php b/core/modules/views/lib/Views/user/Plugin/views/field/Link.php new file mode 100644 index 0000000000000000000000000000000000000000..8166515ef2dde61e7cba99c852cd42dce03b2caa --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Link.php @@ -0,0 +1,74 @@ +additional_fields['uid'] = 'uid'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['text'] = array('default' => '', 'translatable' => TRUE); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + parent::buildOptionsForm($form, $form_state); + } + + // An example of field level access control. + public function access() { + return user_access('access user profiles'); + } + + public function query() { + $this->ensureMyTable(); + $this->add_additional_fields(); + } + + function render($values) { + $value = $this->get_value($values, 'uid'); + return $this->render_link($this->sanitizeValue($value), $values); + } + + function render_link($data, $values) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('view'); + + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "user/" . $data; + + return $text; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/LinkCancel.php b/core/modules/views/lib/Views/user/Plugin/views/field/LinkCancel.php new file mode 100644 index 0000000000000000000000000000000000000000..fb6cc4c5c15a9e4ee3755de3a2c8df3ca35c89d4 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/LinkCancel.php @@ -0,0 +1,43 @@ +{$this->aliases['uid']}; + + // Build a pseudo account object to be able to check the access. + $account = entity_create('user', array()); + $account->uid = $uid; + + if ($uid && user_cancel_access($account)) { + $this->options['alter']['make_link'] = TRUE; + + $text = !empty($this->options['text']) ? $this->options['text'] : t('cancel'); + + $this->options['alter']['path'] = "user/$uid/cancel"; + $this->options['alter']['query'] = drupal_get_destination(); + + return $text; + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/LinkEdit.php b/core/modules/views/lib/Views/user/Plugin/views/field/LinkEdit.php new file mode 100644 index 0000000000000000000000000000000000000000..f9b91f6e8021ba329d2418d5c208fa28261e337c --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/LinkEdit.php @@ -0,0 +1,41 @@ +uid = $data; + + if ($data && user_edit_access($account)) { + $this->options['alter']['make_link'] = TRUE; + + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + + $this->options['alter']['path'] = "user/$data/edit"; + $this->options['alter']['query'] = drupal_get_destination(); + + return $text; + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Mail.php b/core/modules/views/lib/Views/user/Plugin/views/field/Mail.php new file mode 100644 index 0000000000000000000000000000000000000000..64ad0f01c76236e059f4a4ba5d5fb8635f825410 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Mail.php @@ -0,0 +1,56 @@ + 'mailto'); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['link_to_user'] = array( + '#title' => t('Link this field'), + '#type' => 'radios', + '#options' => array( + 0 => t('No link'), + 'user' => t('To the user'), + 'mailto' => t("With a mailto:"), + ), + '#default_value' => $this->options['link_to_user'], + ); + } + + function render_link($data, $values) { + parent::render_link($data, $values); + + if ($this->options['link_to_user'] == 'mailto') { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "mailto:" . $data; + } + + return $data; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Name.php b/core/modules/views/lib/Views/user/Plugin/views/field/Name.php new file mode 100644 index 0000000000000000000000000000000000000000..8ce583d0111b9db3110d1f63b579dd178eb79a62 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Name.php @@ -0,0 +1,98 @@ +options['overwrite_anonymous']) || !empty($this->options['format_username'])) { + $this->additional_fields['uid'] = 'uid'; + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + + $options['overwrite_anonymous'] = array('default' => FALSE, 'bool' => TRUE); + $options['anonymous_text'] = array('default' => '', 'translatable' => TRUE); + $options['format_username'] = array('default' => TRUE, 'bool' => TRUE); + + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + $form['format_username'] = array( + '#title' => t('Use formatted username'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['format_username']), + '#description' => t('If checked, the username will be formatted by the system. If unchecked, it will be displayed raw.'), + '#fieldset' => 'more', + ); + $form['overwrite_anonymous'] = array( + '#title' => t('Overwrite the value to display for anonymous users'), + '#type' => 'checkbox', + '#default_value' => !empty($this->options['overwrite_anonymous']), + '#description' => t('Enable to display different text for anonymous users.'), + '#fieldset' => 'more', + ); + $form['anonymous_text'] = array( + '#title' => t('Text to display for anonymous users'), + '#type' => 'textfield', + '#default_value' => $this->options['anonymous_text'], + '#states' => array( + 'visible' => array( + ':input[name="options[overwrite_anonymous]"]' => array('checked' => TRUE), + ), + ), + '#fieldset' => 'more', + ); + + parent::buildOptionsForm($form, $form_state); + } + + function render_link($data, $values) { + $account = entity_create('user', array()); + $account->uid = $this->get_value($values, 'uid'); + $account->name = $this->get_value($values); + if (!empty($this->options['link_to_user']) || !empty($this->options['overwrite_anonymous'])) { + if (!empty($this->options['overwrite_anonymous']) && !$account->uid) { + // This is an anonymous user, and we're overriting the text. + return check_plain($this->options['anonymous_text']); + } + elseif (!empty($this->options['link_to_user'])) { + $account->name = $this->get_value($values); + return theme('username', array('account' => $account)); + } + } + // If we want a formatted username, do that. + if (!empty($this->options['format_username'])) { + return user_format_name($account); + } + // Otherwise, there's no special handling, so return the data directly. + return $data; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Permissions.php b/core/modules/views/lib/Views/user/Plugin/views/field/Permissions.php new file mode 100644 index 0000000000000000000000000000000000000000..382162f713041bbf5ddfdbc50cbf6a053eca6e7d --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Permissions.php @@ -0,0 +1,91 @@ +additional_fields['uid'] = array('table' => 'users', 'field' => 'uid'); + } + + public function query() { + $this->add_additional_fields(); + $this->field_alias = $this->aliases['uid']; + } + + function pre_render(&$values) { + $uids = array(); + $this->items = array(); + + foreach ($values as $result) { + $uids[] = $this->get_value($result); + } + + if ($uids) { + // Get a list of all the modules implementing a hook_permission() and sort by + // display name. + $module_info = system_get_info('module'); + $modules = array(); + foreach (module_implements('permission') as $module) { + $modules[$module] = $module_info[$module]['name']; + } + asort($modules); + + $permissions = module_invoke_all('permission'); + + $query = db_select('role_permission', 'rp'); + $query->join('users_roles', 'u', 'u.rid = rp.rid'); + $query->fields('u', array('uid', 'rid')); + $query->addField('rp', 'permission'); + $query->condition('u.uid', $uids); + $query->condition('rp.module', array_keys($modules)); + $query->orderBy('rp.permission'); + $result = $query->execute(); + + foreach ($result as $perm) { + $this->items[$perm->uid][$perm->permission]['permission'] = $permissions[$perm->permission]['title']; + } + } + } + + function render_item($count, $item) { + return $item['permission']; + } + + /* + function document_self_tokens(&$tokens) { + $tokens['[' . $this->options['id'] . '-role' . ']'] = t('The name of the role.'); + $tokens['[' . $this->options['id'] . '-rid' . ']'] = t('The role ID of the role.'); + } + + function add_self_tokens(&$tokens, $item) { + $tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role']; + $tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid']; + } + */ + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Picture.php b/core/modules/views/lib/Views/user/Plugin/views/field/Picture.php new file mode 100644 index 0000000000000000000000000000000000000000..2774cfb9a983b7a59c40043a7d3ecefa0646628f --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Picture.php @@ -0,0 +1,131 @@ +additional_fields['uid'] = 'uid'; + $this->additional_fields['name'] = 'name'; + $this->additional_fields['mail'] = 'mail'; + } + + function element_type($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) { + if ($inline) { + return 'span'; + } + if ($none_supported) { + if ($this->options['element_type'] === '0') { + return ''; + } + } + if ($this->options['element_type']) { + return check_plain($this->options['element_type']); + } + if ($default_empty) { + return ''; + } + if (isset($this->definition['element type'])) { + return $this->definition['element type']; + } + + return 'div'; + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_photo_to_profile'] = array('default' => TRUE, 'bool' => TRUE); + $options['image_style'] = array('default' => ''); + return $options; + } + + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + $form['link_photo_to_profile'] = array( + '#title' => t("Link to user's profile"), + '#description' => t("Link the user picture to the user's profile"), + '#type' => 'checkbox', + '#default_value' => $this->options['link_photo_to_profile'], + ); + + if (module_exists('image')) { + $styles = image_styles(); + $style_options = array('' => t('Default')); + foreach ($styles as $style) { + $style_options[$style['name']] = $style['name']; + } + + $form['image_style'] = array( + '#title' => t('Image style'), + '#description' => t('Using Default will use the site-wide image style for user pictures set in the Account settings.', array('!account-settings' => url('admin/config/people/accounts', array('fragment' => 'edit-personalization')))), + '#type' => 'select', + '#options' => $style_options, + '#default_value' => $this->options['image_style'], + ); + } + } + + function render($values) { + if ($this->options['image_style'] && module_exists('image')) { + // @todo: Switch to always using theme('user_picture') when it starts + // supporting image styles. See http://drupal.org/node/1021564 + if ($picture_fid = $this->get_value($values)) { + $picture = file_load($picture_fid); + $picture_filepath = $picture->uri; + } + else { + $picture_filepath = variable_get('user_picture_default', ''); + } + if (file_valid_uri($picture_filepath)) { + $output = theme('image_style', array('style_name' => $this->options['image_style'], 'path' => $picture_filepath)); + if ($this->options['link_photo_to_profile'] && user_access('access user profiles')) { + $uid = $this->get_value($values, 'uid'); + $output = l($output, "user/$uid", array('html' => TRUE)); + } + } + else { + $output = ''; + } + } + else { + // Fake an account object. + $account = entity_create('user', array()); + if ($this->options['link_photo_to_profile']) { + // Prevent template_preprocess_user_picture from adding a link + // by not setting the uid. + $account->uid = $this->get_value($values, 'uid'); + } + $account->name = $this->get_value($values, 'name'); + $account->mail = $this->get_value($values, 'mail'); + $account->picture = $this->get_value($values); + $output = theme('user_picture', array('account' => $account)); + } + + return $output; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/Roles.php b/core/modules/views/lib/Views/user/Plugin/views/field/Roles.php new file mode 100644 index 0000000000000000000000000000000000000000..f80988e3dd80430ca8c1f43851f1eb5aa5d93538 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/Roles.php @@ -0,0 +1,79 @@ +additional_fields['uid'] = array('table' => 'users', 'field' => 'uid'); + } + + public function query() { + $this->add_additional_fields(); + $this->field_alias = $this->aliases['uid']; + } + + function pre_render(&$values) { + $uids = array(); + $this->items = array(); + + foreach ($values as $result) { + $uids[] = $this->get_value($result); + } + + if ($uids) { + $query = db_select('role', 'r'); + $query->join('users_roles', 'u', 'u.rid = r.rid'); + $query->addField('r', 'name'); + $query->fields('u', array('uid', 'rid')); + $query->condition('u.uid', $uids); + $query->orderBy('r.name'); + $result = $query->execute(); + foreach ($result as $role) { + $this->items[$role->uid][$role->rid]['role'] = check_plain($role->name); + $this->items[$role->uid][$role->rid]['rid'] = $role->rid; + } + } + } + + function render_item($count, $item) { + return $item['role']; + } + + function document_self_tokens(&$tokens) { + $tokens['[' . $this->options['id'] . '-role' . ']'] = t('The name of the role.'); + $tokens['[' . $this->options['id'] . '-rid' . ']'] = t('The role machine-name of the role.'); + } + + function add_self_tokens(&$tokens, $item) { + if (!empty($item['role'])) { + $tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role']; + $tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid']; + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/field/User.php b/core/modules/views/lib/Views/user/Plugin/views/field/User.php new file mode 100644 index 0000000000000000000000000000000000000000..1fee9d2fd60849bc770b97eb37ef8c71a5766fa3 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/field/User.php @@ -0,0 +1,68 @@ +options['link_to_user'])) { + $this->additional_fields['uid'] = 'uid'; + } + } + + protected function defineOptions() { + $options = parent::defineOptions(); + $options['link_to_user'] = array('default' => TRUE, 'bool' => TRUE); + return $options; + } + + /** + * Provide link to node option + */ + public function buildOptionsForm(&$form, &$form_state) { + $form['link_to_user'] = array( + '#title' => t('Link this field to its user'), + '#description' => t("Enable to override this field's links."), + '#type' => 'checkbox', + '#default_value' => $this->options['link_to_user'], + ); + parent::buildOptionsForm($form, $form_state); + } + + function render_link($data, $values) { + if (!empty($this->options['link_to_user']) && user_access('access user profiles') && ($uid = $this->get_value($values, 'uid')) && $data !== NULL && $data !== '') { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "user/" . $uid; + } + return $data; + } + + function render($values) { + $value = $this->get_value($values); + return $this->render_link($this->sanitizeValue($value), $values); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/filter/Current.php b/core/modules/views/lib/Views/user/Plugin/views/filter/Current.php new file mode 100644 index 0000000000000000000000000000000000000000..874e871a853b1ad0fc941d7f9bd426d98708bff8 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/filter/Current.php @@ -0,0 +1,53 @@ +value_value = t('Is the logged in user'); + } + + public function query() { + $this->ensureMyTable(); + + $field = $this->tableAlias . '.' . $this->realField . ' '; + $or = db_or(); + + if (empty($this->value)) { + $or->condition($field, '***CURRENT_USER***', '<>'); + if ($this->accept_null) { + $or->isNull($field); + } + } + else { + $or->condition($field, '***CURRENT_USER***', '='); + } + $this->query->add_where($this->options['group'], $or); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/filter/Name.php b/core/modules/views/lib/Views/user/Plugin/views/filter/Name.php new file mode 100644 index 0000000000000000000000000000000000000000..857a1df72422ef1a8cfb7661a74ecbaa03e2e3dc --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/filter/Name.php @@ -0,0 +1,173 @@ +value) { + $result = entity_load_multiple_by_properties('user', array('uid' => $this->value)); + foreach ($result as $account) { + if ($account->uid) { + $values[] = $account->name; + } + else { + $values[] = 'Anonymous'; // Intentionally NOT translated. + } + } + } + + sort($values); + $default_value = implode(', ', $values); + $form['value'] = array( + '#type' => 'textfield', + '#title' => t('Usernames'), + '#description' => t('Enter a comma separated list of user names.'), + '#default_value' => $default_value, + '#autocomplete_path' => 'admin/views/ajax/autocomplete/user', + ); + + if (!empty($form_state['exposed']) && !isset($form_state['input'][$this->options['expose']['identifier']])) { + $form_state['input'][$this->options['expose']['identifier']] = $default_value; + } + } + + function value_validate($form, &$form_state) { + $values = drupal_explode_tags($form_state['values']['options']['value']); + $uids = $this->validate_user_strings($form['value'], $values); + + if ($uids) { + $form_state['values']['options']['value'] = $uids; + } + } + + public function acceptExposedInput($input) { + $rc = parent::acceptExposedInput($input); + + if ($rc) { + // If we have previously validated input, override. + if (isset($this->validated_exposed_input)) { + $this->value = $this->validated_exposed_input; + } + } + + return $rc; + } + + public function validateExposed(&$form, &$form_state) { + if (empty($this->options['exposed'])) { + return; + } + + if (empty($this->options['expose']['identifier'])) { + return; + } + + $identifier = $this->options['expose']['identifier']; + $input = $form_state['values'][$identifier]; + + if ($this->options['is_grouped'] && isset($this->options['group_info']['group_items'][$input])) { + $this->operator = $this->options['group_info']['group_items'][$input]['operator']; + $input = $this->options['group_info']['group_items'][$input]['value']; + } + + $values = drupal_explode_tags($input); + + if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) { + $uids = $this->validate_user_strings($form[$identifier], $values); + } + else { + $uids = FALSE; + } + + if ($uids) { + $this->validated_exposed_input = $uids; + } + } + + /** + * Validate the user string. Since this can come from either the form + * or the exposed filter, this is abstracted out a bit so it can + * handle the multiple input sources. + */ + function validate_user_strings(&$form, $values) { + $uids = array(); + $placeholders = array(); + $args = array(); + $results = array(); + foreach ($values as $value) { + if (strtolower($value) == 'anonymous') { + $uids[] = 0; + } + else { + $missing[strtolower($value)] = TRUE; + $args[] = $value; + $placeholders[] = "'%s'"; + } + } + + if (!$args) { + return $uids; + } + + $result = entity_load_multiple_by_properties('user', array('name' => $args)); + foreach ($result as $account) { + unset($missing[strtolower($account->name)]); + $uids[] = $account->uid; + } + + if ($missing) { + form_error($form, format_plural(count($missing), 'Unable to find user: @users', 'Unable to find users: @users', array('@users' => implode(', ', array_keys($missing))))); + } + + return $uids; + } + + function value_submit($form, &$form_state) { + // prevent array filter from removing our anonymous user. + } + + // Override to do nothing. + function get_value_options() { } + + public function adminSummary() { + // set up $this->value_options for the parent summary + $this->value_options = array(); + + if ($this->value) { + $result = entity_load_multiple_by_properties('user', array('uid' => $this->value)); + foreach ($result as $account) { + if ($account->uid) { + $this->value_options[$account->uid] = $account->name; + } + else { + $this->value_options[$account->uid] = 'Anonymous'; // Intentionally NOT translated. + } + } + } + + return parent::adminSummary(); + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/filter/Permissions.php b/core/modules/views/lib/Views/user/Plugin/views/filter/Permissions.php new file mode 100644 index 0000000000000000000000000000000000000000..a53835cf6d642114109dd54c13bf4f0bc198aa89 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/filter/Permissions.php @@ -0,0 +1,47 @@ +value_options = array(); + foreach ($modules as $module => $display_name) { + if ($permissions = module_invoke($module, 'permission')) { + foreach ($permissions as $perm => $perm_item) { + // @todo: group by module but views_handler_filter_many_to_one does not support this. + $this->value_options[$perm] = check_plain(strip_tags($perm_item['title'])); + } + } + } + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/filter/Roles.php b/core/modules/views/lib/Views/user/Plugin/views/filter/Roles.php new file mode 100644 index 0000000000000000000000000000000000000000..8b17873945a5aaa702cae0de0fd264acdf54abf3 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/filter/Roles.php @@ -0,0 +1,40 @@ +value_options = user_roles(TRUE); + unset($this->value_options[DRUPAL_AUTHENTICATED_RID]); + } + + /** + * Override empty and not empty operator labels to be clearer for user roles. + */ + function operators() { + $operators = parent::operators(); + $operators['empty']['title'] = t("Only has the 'authenticated user' role"); + $operators['not empty']['title'] = t("Has roles in addition to 'authenticated user'"); + return $operators; + } + +} diff --git a/core/modules/views/lib/Views/user/Plugin/views/row/View.php b/core/modules/views/lib/Views/user/Plugin/views/row/View.php new file mode 100644 index 0000000000000000000000000000000000000000..17375b9ab7279b50990056aee81dced8e0c041c7 --- /dev/null +++ b/core/modules/views/lib/Views/user/Plugin/views/row/View.php @@ -0,0 +1,41 @@ + 'uid', + 'table' => 'users', + 'field' => 'uid', + 'exclude' => TRUE, + 'link_to_user' => FALSE, + 'alter' => array( + 'alter_text' => TRUE, + 'text' => 'user/[uid]' + ) + ); + + /** + * Set default values for the filters. + */ + protected $filters = array( + 'status' => array( + 'value' => TRUE, + 'table' => 'users', + 'field' => 'status' + ) + ); + + /** + * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::default_display_options(). + */ + protected function default_display_options() { + $display_options = parent::default_display_options(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + $display_options['access']['perm'] = 'access user profiles'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: User: Name */ + $display_options['fields']['name']['id'] = 'name'; + $display_options['fields']['name']['table'] = 'users'; + $display_options['fields']['name']['field'] = 'name'; + $display_options['fields']['name']['label'] = ''; + $display_options['fields']['name']['alter']['alter_text'] = 0; + $display_options['fields']['name']['alter']['make_link'] = 0; + $display_options['fields']['name']['alter']['absolute'] = 0; + $display_options['fields']['name']['alter']['trim'] = 0; + $display_options['fields']['name']['alter']['word_boundary'] = 0; + $display_options['fields']['name']['alter']['ellipsis'] = 0; + $display_options['fields']['name']['alter']['strip_tags'] = 0; + $display_options['fields']['name']['alter']['html'] = 0; + $display_options['fields']['name']['hide_empty'] = 0; + $display_options['fields']['name']['empty_zero'] = 0; + $display_options['fields']['name']['link_to_user'] = 1; + $display_options['fields']['name']['overwrite_anonymous'] = 0; + + return $display_options; + } + +} diff --git a/core/modules/views/modules/aggregator.views.inc b/core/modules/views/modules/aggregator.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..a61025360b8bcd7daae331cc32d5851c832bb2d1 --- /dev/null +++ b/core/modules/views/modules/aggregator.views.inc @@ -0,0 +1,358 @@ + 'iid', + 'title' => t('Aggregator item'), + 'help' => t("Aggregator items are imported from external RSS and Atom news feeds."), + ); + + // Fields + + // iid + $data['aggregator_item']['iid'] = array( + 'title' => t('Item ID'), + 'help' => t('The unique ID of the aggregator item.'), // The help that appears on the UI, + // Information for displaying the iid + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + // Information for accepting a iid as an argument + 'argument' => array( + 'id' => 'aggregator_iid', + 'name field' => 'title', // the field to display in the summary. + 'numeric' => TRUE, + ), + // Information for accepting a nid as a filter + 'filter' => array( + 'id' => 'numeric', + ), + // Information for sorting on a nid. + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + $data['aggregator_item']['title'] = array( + 'title' => t('Title'), // The item it appears as on the UI, + 'help' => t('The title of the aggregator item.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'aggregator_title_link', + 'extra' => array('link'), + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + ); + + // link + $data['aggregator_item']['link'] = array( + 'title' => t('Link'), // The item it appears as on the UI, + 'help' => t('The link to the original source URL of the item.'), + 'field' => array( + 'id' => 'url', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + ); + + // author + $data['aggregator_item']['author'] = array( + 'title' => t('Author'), // The item it appears as on the UI, + 'help' => t('The author of the original imported item.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'aggregator_xss', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // guid + $data['aggregator_item']['guid'] = array( + 'title' => t('GUID'), // The item it appears as on the UI, + 'help' => t('The guid of the original imported item.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'xss', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // feed body + $data['aggregator_item']['description'] = array( + 'title' => t('Body'), // The item it appears as on the UI, + 'help' => t('The actual content of the imported item.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'aggregator_xss', + 'click sortable' => FALSE, + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + ); + + // item timestamp + $data['aggregator_item']['timestamp'] = array( + 'title' => t('Timestamp'), // The item it appears as on the UI, + 'help' => t('The date the original feed item was posted. (With some feeds, this will be the date it was imported.)'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'date', + ), + 'argument' => array( + 'id' => 'date', + ), + ); + + // Aggregator feed table + + $data['aggregator_feed']['table']['group'] = t('Aggregator feed'); + + // Explain how this table joins to others. + $data['aggregator_feed']['table']['join'] = array( + 'aggregator_item' => array( + 'left_field' => 'fid', + 'field' => 'fid', + ), + ); + + // fid + $data['aggregator_feed']['fid'] = array( + 'title' => t('Feed ID'), + 'help' => t('The unique ID of the aggregator feed.'), // The help that appears on the UI, + // Information for displaying the fid + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + // Information for accepting a fid as an argument + 'argument' => array( + 'id' => 'aggregator_fid', + 'name field' => 'title', // the field to display in the summary. + 'numeric' => TRUE, + ), + // Information for accepting a nid as a filter + 'filter' => array( + 'id' => 'numeric', + ), + // Information for sorting on a fid. + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + $data['aggregator_feed']['title'] = array( + 'title' => t('Title'), // The item it appears as on the UI, + 'help' => t('The title of the aggregator feed.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'aggregator_title_link', + 'extra' => array('link'), + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // link + $data['aggregator_feed']['link'] = array( + 'title' => t('Link'), // The item it appears as on the UI, + 'help' => t('The link to the source URL of the feed.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'url', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // feed last updated + $data['aggregator_feed']['checked'] = array( + 'title' => t('Last checked'), // The item it appears as on the UI, + 'help' => t('The date the feed was last checked for new content.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + 'argument' => array( + 'id' => 'date', + ), + ); + + // feed description + $data['aggregator_feed']['description'] = array( + 'title' => t('Description'), // The item it appears as on the UI, + 'help' => t('The description of the aggregator feed.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'xss', + 'click sortable' => FALSE, + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // feed last updated + $data['aggregator_feed']['modified'] = array( + 'title' => t('Last modified'), // The item it appears as on the UI, + 'help' => t('The date of the most recent new content on the feed.'), + // Information for displaying a title as a field + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'date', + ), + 'argument' => array( + 'id' => 'date', + ), + ); + + // Aggregator category feed table + + $data['aggregator_category_feed']['table']['join'] = array( + 'aggregator_item' => array( + 'left_field' => 'fid', + 'field' => 'fid', + ), + ); + + // Aggregator category table + + $data['aggregator_category']['table']['group'] = t('Aggregator category'); + + $data['aggregator_category']['table']['join'] = array( + 'aggregator_item' => array( + 'left_table' => 'aggregator_category_feed', + 'left_field' => 'cid', + 'field' => 'cid', + ), + ); + + // cid + $data['aggregator_category']['cid'] = array( + 'title' => t('Category ID'), + 'help' => t('The unique ID of the aggregator category.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'aggregator_category_cid', + 'name field' => 'title', + 'numeric' => TRUE, + ), + 'filter' => array( + 'id' => 'aggregator_category_cid', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + $data['aggregator_category']['title'] = array( + 'title' => t('Category'), + 'help' => t('The title of the aggregator category.'), + 'field' => array( + 'id' => 'aggregator_category', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/book.views.inc b/core/modules/views/modules/book.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..6e2b890a8e595f06ca0aa3504575d80faa5e4c40 --- /dev/null +++ b/core/modules/views/modules/book.views.inc @@ -0,0 +1,113 @@ + array( + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + + $data['book']['bid'] = array( + 'title' => t('Top level book'), + 'help' => t('The book the node is in.'), + 'relationship' => array( + 'base' => 'node', + 'id' => 'standard', + 'label' => t('Book'), + ), + // There is no argument here; if you need an argument, add the relationship + // and use the node: nid argument. + ); + + // menu_links table -- this is aliased so we can get just book relations + + // Book hierarchy and weight data are now in {menu_links}. + $data['book_menu_links']['table']['group'] = t('Book'); + $data['book_menu_links']['table']['join'] = array( + 'node' => array( + 'table' => 'menu_links', + 'left_table' => 'book', + 'left_field' => 'mlid', + 'field' => 'mlid', + ), + ); + + $data['book_menu_links']['weight'] = array( + 'title' => t('Weight'), + 'help' => t('The weight of the book page.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + $data['book_menu_links']['depth'] = array( + 'title' => t('Depth'), + 'help' => t('The depth of the book page in the hierarchy; top level books have a depth of 1.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'standard', + ), + ); + + $data['book_menu_links']['p'] = array( + 'title' => t('Hierarchy'), + 'help' => t('The order of pages in the book hierarchy.'), + 'sort' => array( + 'id' => 'menu_hierarchy', + ), + ); + + // book_parent table -- this is an alias of the book table which + // represents the parent book. + + // The {book} record for the parent node. + $data['book_parent']['table']['group'] = t('Book'); + $data['book_parent']['table']['join'] = array( + 'node' => array( + 'table' => 'book', + 'left_table' => 'book_menu_links', + 'left_field' => 'plid', + 'field' => 'mlid', + ), + ); + + $data['book_parent']['nid'] = array( + 'title' => t('Parent'), + 'help' => t('The parent book node.'), + 'relationship' => array( + 'base' => 'node', + 'base field' => 'nid', + 'id' => 'standard', + 'label' => t('Book parent'), + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/comment.views.inc b/core/modules/views/modules/comment.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..79c3bc32479065910ef25ec8b7ab6e73c2b06dc5 --- /dev/null +++ b/core/modules/views/modules/comment.views.inc @@ -0,0 +1,580 @@ + 'cid', + 'title' => t('Comment'), + 'help' => t("Comments are responses to node content."), + 'access query tag' => 'comment_access', + ); + $data['comment']['table']['entity type'] = 'comment'; + + // Fields + + // subject + $data['comment']['subject'] = array( + 'title' => t('Title'), + 'help' => t('The title of the comment.'), + 'field' => array( + 'id' => 'comment', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // cid + $data['comment']['cid'] = array( + 'title' => t('ID'), + 'help' => t('The comment ID of the field'), + 'field' => array( + 'id' => 'comment', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'numeric', + ), + ); + + // name (of comment author) + $data['comment']['name'] = array( + 'title' => t('Author'), + 'help' => t("The name of the comment's author. Can be rendered as a link to the author's homepage."), + 'field' => array( + 'id' => 'comment_username', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // homepage + $data['comment']['homepage'] = array( + 'title' => t("Author's website"), + 'help' => t("The website address of the comment's author. Can be rendered as a link. Will be empty if the author is a registered user."), + 'field' => array( + 'id' => 'url', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // hostname + $data['comment']['hostname'] = array( + 'title' => t('Hostname'), + 'help' => t('Hostname of user that posted the comment.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // mail + $data['comment']['mail'] = array( + 'title' => t('Mail'), + 'help' => t('Email of user that posted the comment. Will be empty if the author is a registered user.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // created (when comment was posted) + $data['comment']['created'] = array( + 'title' => t('Post date'), + 'help' => t('Date and time of when the comment was created.'), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // Langcode field + if (module_exists('language')) { + $data['comment']['langcode'] = array( + 'title' => t('Language'), + 'help' => t('The language the comment is in.'), + 'field' => array( + 'id' => 'language', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'language', + ), + 'argument' => array( + 'id' => 'language', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + } + + // changed (when comment was last updated) + $data['comment']['changed'] = array( + 'title' => t('Updated date'), + 'help' => t('Date and time of when the comment was last updated.'), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + $data['comment']['changed_fulldata'] = array( + 'title' => t('Created date'), + 'help' => t('Date in the form of CCYYMMDD.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_fulldate', + ), + ); + + $data['comment']['changed_year_month'] = array( + 'title' => t('Created year + month'), + 'help' => t('Date in the form of YYYYMM.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_year_month', + ), + ); + + $data['comment']['changed_year'] = array( + 'title' => t('Created year'), + 'help' => t('Date in the form of YYYY.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_year', + ), + ); + + $data['comment']['changed_month'] = array( + 'title' => t('Created month'), + 'help' => t('Date in the form of MM (01 - 12).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_month', + ), + ); + + $data['comment']['changed_day'] = array( + 'title' => t('Created day'), + 'help' => t('Date in the form of DD (01 - 31).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_day', + ), + ); + + $data['comment']['changed_week'] = array( + 'title' => t('Created week'), + 'help' => t('Date in the form of WW (01 - 53).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_week', + ), + ); + + // status (approved or not) + $data['comment']['status'] = array( + 'title' => t('Approved'), + 'help' => t('Whether the comment is approved (or still in the moderation queue).'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'approved-not-approved' => array(t('Approved'), t('Not Approved')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Approved comment'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // link to view comment + $data['comment']['view_comment'] = array( + 'field' => array( + 'title' => t('View link'), + 'help' => t('Provide a simple link to view the comment.'), + 'id' => 'comment_link', + ), + ); + + // link to edit comment + $data['comment']['edit_comment'] = array( + 'field' => array( + 'title' => t('Edit link'), + 'help' => t('Provide a simple link to edit the comment.'), + 'id' => 'comment_link_edit', + ), + ); + + // link to delete comment + $data['comment']['delete_comment'] = array( + 'field' => array( + 'title' => t('Delete link'), + 'help' => t('Provide a simple link to delete the comment.'), + 'id' => 'comment_link_delete', + ), + ); + + // link to approve comment + $data['comment']['approve_comment'] = array( + 'field' => array( + 'title' => t('Approve link'), + 'help' => t('Provide a simple link to approve the comment.'), + 'id' => 'comment_link_approve', + ), + ); + + // link to reply to comment + $data['comment']['replyto_comment'] = array( + 'field' => array( + 'title' => t('Reply-to link'), + 'help' => t('Provide a simple link to reply to the comment.'), + 'id' => 'comment_link_reply', + ), + ); + + $data['comment']['thread'] = array( + 'field' => array( + 'title' => t('Depth'), + 'help' => t('Display the depth of the comment if it is threaded.'), + 'id' => 'comment_depth', + ), + 'sort' => array( + 'title' => t('Thread'), + 'help' => t('Sort by the threaded order. This will keep child comments together with their parents.'), + 'id' => 'comment_thread', + ), + ); + + $data['comment']['nid'] = array( + 'title' => t('Nid'), + 'help' => t('The node ID to which the comment is a reply to.'), + 'relationship' => array( + 'title' => t('Content'), + 'help' => t('The content to which the comment is a reply to.'), + 'base' => 'node', + 'base field' => 'nid', + 'id' => 'standard', + 'label' => t('Content'), + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'field' => array( + 'id' => 'numeric', + ), + ); + + $data['comment']['uid'] = array( + 'title' => t('Author uid'), + 'help' => t('If you need more fields than the uid add the comment: author relationship'), + 'relationship' => array( + 'title' => t('Author'), + 'help' => t("The User ID of the comment's author."), + 'base' => 'users', + 'base field' => 'uid', + 'id' => 'standard', + 'label' => t('author'), + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'field' => array( + 'id' => 'user', + ), + ); + + $data['comment']['pid'] = array( + 'title' => t('Parent CID'), + 'help' => t('The Comment ID of the parent comment.'), + 'field' => array( + 'id' => 'standard', + ), + 'relationship' => array( + 'title' => t('Parent comment'), + 'help' => t('The parent comment.'), + 'base' => 'comment', + 'base field' => 'cid', + 'id' => 'standard', + 'label' => t('Parent comment'), + ), + ); + + // node_comment_statistics table + + // define the group + $data['node_comment_statistics']['table']['group'] = t('Content'); + + // joins + $data['node_comment_statistics']['table']['join'] = array( + //...to the node table + 'node' => array( + 'type' => 'INNER', + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + + // last_comment_timestamp + $data['node_comment_statistics']['last_comment_timestamp'] = array( + 'title' => t('Last comment time'), + 'help' => t('Date and time of when the last comment was posted.'), + 'field' => array( + 'id' => 'comment_last_timestamp', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // last_comment_name (author's name) + $data['node_comment_statistics']['last_comment_name'] = array( + 'title' => t("Last comment author"), + 'help' => t('The name of the author of the last posted comment.'), + 'field' => array( + 'id' => 'comment_ncs_last_comment_name', + 'click sortable' => TRUE, + 'no group by' => TRUE, + ), + 'sort' => array( + 'id' => 'comment_ncs_last_comment_name', + 'no group by' => TRUE, + ), + ); + + // comment_count + $data['node_comment_statistics']['comment_count'] = array( + 'title' => t('Comment count'), + 'help' => t('The number of comments a node has.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'standard', + ), + ); + + // last_comment_timestamp + $data['node_comment_statistics']['last_updated'] = array( + 'title' => t('Updated/commented date'), + 'help' => t('The most recent of last comment posted or node updated time.'), + 'field' => array( + 'id' => 'comment_ncs_last_updated', + 'click sortable' => TRUE, + 'no group by' => TRUE, + ), + 'sort' => array( + 'id' => 'comment_ncs_last_updated', + 'no group by' => TRUE, + ), + 'filter' => array( + 'id' => 'comment_ncs_last_updated', + ), + ); + + $data['node_comment_statistics']['cid'] = array( + 'title' => t('Last comment CID'), + 'help' => t('Display the last comment of a node'), + 'relationship' => array( + 'title' => t('Last Comment'), + 'help' => t('The last comment of a node.'), + 'group' => t('Comment'), + 'base' => 'comment', + 'base field' => 'cid', + 'id' => 'standard', + 'label' => t('Last Comment'), + ), + ); + + // last_comment_uid + $data['node_comment_statistics']['last_comment_uid'] = array( + 'title' => t('Last comment uid'), + 'help' => t('The User ID of the author of the last comment of a node.'), + 'relationship' => array( + 'title' => t('Last comment author'), + 'base' => 'users', + 'base field' => 'uid', + 'id' => 'standard', + 'label' => t('Last comment author'), + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'field' => array( + 'id' => 'numeric', + ), + ); + + return $data; +} + +/** + * Use views_data_alter to add items to the node table that are + * relevant to comments. + */ +function comment_views_data_alter(&$data) { + // new comments + $data['node']['new_comments'] = array( + 'title' => t('New comments'), + 'help' => t('The number of new comments on the node.'), + 'field' => array( + 'id' => 'node_new_comments', + 'no group by' => TRUE, + ), + ); + + $data['node']['comments_link'] = array( + 'field' => array( + 'title' => t('Add comment link'), + 'help' => t('Display the standard add comment link used on regular nodes, which will only display if the viewing user has access to add a comment.'), + 'id' => 'comment_node_link', + ), + ); + + // Comment status of the node + $data['node']['comment'] = array( + 'title' => t('Comment status'), + 'help' => t('Whether comments are enabled or disabled on the node.'), + 'field' => array( + 'id' => 'node_comment', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'node_comment', + ), + ); + + $data['node']['uid_touch'] = array( + 'title' => t('User posted or commented'), + 'help' => t('Display nodes only if a user posted the node or commented on the node.'), + 'argument' => array( + 'field' => 'uid', + 'name table' => 'users', + 'name field' => 'name', + 'id' => 'argument_comment_user_uid', + 'no group by' => TRUE, + ), + 'filter' => array( + 'field' => 'uid', + 'name table' => 'users', + 'name field' => 'name', + 'id' => 'comment_user_uid', + ), + ); + + $data['node']['cid'] = array( + 'title' => t('Comments of the node'), + 'help' => t('Relate all comments on the node. This will create 1 duplicate record for every comment. Usually if you need this it is better to create a comment view.'), + 'relationship' => array( + 'group' => t('Comment'), + 'label' => t('Comments'), + 'base' => 'comment', + 'base field' => 'nid', + 'relationship field' => 'nid', + 'id' => 'standard', + ), + ); + +} diff --git a/core/modules/views/modules/contact.views.inc b/core/modules/views/modules/contact.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..307ffbc176d134cfd8e24ce40ff439853ef29f30 --- /dev/null +++ b/core/modules/views/modules/contact.views.inc @@ -0,0 +1,21 @@ + array( + 'title' => t('Link to contact page'), + 'help' => t('Provide a simple link to the user contact page.'), + 'id' => 'contact_link', + ), + ); +} diff --git a/core/modules/views/modules/field.views.inc b/core/modules/views/modules/field.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..1629adddf50b4de05e5994e67289e849a62af5a9 --- /dev/null +++ b/core/modules/views/modules/field.views.inc @@ -0,0 +1,461 @@ + $entity_type) { + foreach ($entity_type as $bundle) { + if (isset($bundle[$field_name])) { + $label_counter[$bundle[$field_name]['label']] = isset($label_counter[$bundle[$field_name]['label']]) ? ++$label_counter[$bundle[$field_name]['label']] : 1; + $all_labels[$entity_name][$bundle[$field_name]['label']] = TRUE; + } + } + } + if (empty($label_counter)) { + return array($field_name, $all_labels); + } + // Sort the field lables by it most used label and return the most used one. + arsort($label_counter); + $label_counter = array_keys($label_counter); + return array($label_counter[0], $all_labels); +} + +/** + * Default views data implementation for a field. + */ +function field_views_field_default_views_data($field) { + $field_types = field_info_field_types(); + + // Check the field module is available. + if (!isset($field_types[$field['type']])) { + return; + } + + $data = array(); + + $current_table = _field_sql_storage_tablename($field); + $revision_table = _field_sql_storage_revision_tablename($field); + + // The list of entity:bundle that this field is used in. + $bundles_names = array(); + $supports_revisions = FALSE; + $entity_tables = array(); + $current_tables = array(); + $revision_tables = array(); + $groups = array(); + + $group_name = count($field['bundles']) > 1 ? t('Field') : NULL; + + // Build the relationships between the field table and the entity tables. + foreach ($field['bundles'] as $entity => $bundles) { + $entity_info = entity_get_info($entity); + $groups[$entity] = $entity_info['label']; + + // Override Node to Content. + if ($groups[$entity] == t('Node')) { + $groups[$entity] = t('Content'); + } + + // If only one bundle use this as the default name. + if (empty($group_name)) { + $group_name = $groups[$entity]; + } + + $entity_tables[$entity_info['base table']] = $entity; + $current_tables[$entity] = $entity_info['base table']; + if (isset($entity_info['revision table'])) { + $entity_tables[$entity_info['revision table']] = $entity; + $revision_tables[$entity] = $entity_info['revision table']; + } + + $data[$current_table]['table']['join'][$entity_info['base table']] = array( + 'left_field' => $entity_info['entity keys']['id'], + 'field' => 'entity_id', + 'extra' => array( + array('field' => 'entity_type', 'value' => $entity), + array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE), + ), + ); + + if (!empty($entity_info['entity keys']['revision']) && !empty($entity_info['revision table'])) { + $data[$revision_table]['table']['join'][$entity_info['revision table']] = array( + 'left_field' => $entity_info['entity keys']['revision'], + 'field' => 'revision_id', + 'extra' => array( + array('field' => 'entity_type', 'value' => $entity), + array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE), + ), + ); + + $supports_revisions = TRUE; + } + + foreach ($bundles as $bundle) { + $bundles_names[] = t('@entity:@bundle', array('@entity' => $entity, '@bundle' => $bundle)); + } + } + + $tables = array(); + $tables[FIELD_LOAD_CURRENT] = $current_table; + if ($supports_revisions) { + $tables[FIELD_LOAD_REVISION] = $revision_table; + } + + $add_fields = array('delta', 'langcode', 'bundle'); + foreach ($field['columns'] as $column_name => $attributes) { + $add_fields[] = _field_sql_storage_columnname($field['field_name'], $column_name); + } + + // Note: we don't have a label available here, because we are at the field + // level, not at the instance level. So we just go through all instances + // and take the one which is used the most frequently. + $field_name = $field['field_name']; + list($label, $all_labels) = field_views_field_label($field_name); + foreach ($tables as $type => $table) { + if ($type == FIELD_LOAD_CURRENT) { + $group = $group_name; + $old_column = 'entity_id'; + $column = $field['field_name']; + } + else { + $group = t('@group (historical data)', array('@group' => $group_name)); + $old_column = 'revision_id'; + $column = $field['field_name'] . '-' . $old_column; + } + + $data[$table][$column] = array( + 'group' => $group, + 'title' => $label, + 'title short' => $label, + 'help' => t('Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))), + ); + + // Go through and create a list of aliases for all possible combinations of + // entity type + name. + $aliases = array(); + $also_known = array(); + foreach ($all_labels as $entity_name => $labels) { + foreach ($labels as $label_name => $true) { + if ($type == FIELD_LOAD_CURRENT) { + if ($group_name != $groups[$entity_name] || $label != $label_name) { + $aliases[] = array( + 'base' => $current_tables[$entity_name], + 'group' => $groups[$entity_name], + 'title' => $label_name, + 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)), + ); + } + $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $label_name)); + } + else { + if ($group_name != $groups[$entity_name] && $label != $label_name && isset($revision_tables[$entity_name])) { + $aliases[] = array( + 'base' => $revision_tables[$entity_name], + 'group' => t('@group (historical data)', array('@group' => $groups[$entity_name])), + 'title' => $label_name, + 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)), + ); + } + $also_known[] = t('@group (historical data): @field', array('@group' => $groups[$entity_name], '@field' => $label_name)); + } + } + } + if ($aliases) { + $data[$table][$column]['aliases'] = $aliases; + $data[$table][$column]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known))); + } + + $keys = array_keys($field['columns']); + $real_field = reset($keys); + $data[$table][$column]['field'] = array( + 'table' => $table, + 'id' => 'field', + 'click sortable' => TRUE, + 'field_name' => $field['field_name'], + // Provide a real field for group by. + 'real field' => $column . '_' . $real_field, + 'additional fields' => $add_fields, + 'entity_tables' => $entity_tables, + // Default the element type to div, let the UI change it if necessary. + 'element type' => 'div', + 'is revision' => $type == FIELD_LOAD_REVISION, + ); + } + + foreach ($field['columns'] as $column => $attributes) { + $allow_sort = TRUE; + + // Identify likely filters and arguments for each column based on field type. + switch ($attributes['type']) { + case 'int': + case 'mediumint': + case 'tinyint': + case 'bigint': + case 'serial': + case 'numeric': + case 'float': + $filter = 'numeric'; + $argument = 'numeric'; + $sort = 'standard'; + break; + case 'text': + case 'blob': + // It does not make sense to sort by blob or text. + $allow_sort = FALSE; + default: + $filter = 'string'; + $argument = 'string'; + $sort = 'standard'; + break; + } + + if (count($field['columns']) == 1 || $column == 'value') { + $title = t('@label (!name)', array('@label' => $label, '!name' => $field['field_name'])); + $title_short = $label; + } + else { + $title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $column)); + $title_short = t('@label:!column', array('@label' => $label, '!column' => $column)); + } + + foreach ($tables as $type => $table) { + if ($type == FIELD_LOAD_CURRENT) { + $group = $group_name; + } + else { + $group = t('@group (historical data)', array('@group' => $group_name)); + } + $column_real_name = $field['storage']['details']['sql'][$type][$table][$column]; + + // Load all the fields from the table by default. + $additional_fields = array_values($field['storage']['details']['sql'][$type][$table]); + + $data[$table][$column_real_name] = array( + 'group' => $group, + 'title' => $title, + 'title short' => $title_short, + 'help' => t('Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))), + ); + + // Go through and create a list of aliases for all possible combinations of + // entity type + name. + $aliases = array(); + $also_known = array(); + foreach ($all_labels as $entity_name => $labels) { + foreach ($labels as $label_name => $true) { + if ($group_name != $groups[$entity_name] || $label != $label_name) { + if (count($field['columns']) == 1 || $column == 'value') { + $alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field['field_name'])); + } + else { + $alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field['field_name'], '!column' => $column)); + } + $aliases[] = array( + 'group' => $groups[$entity_name], + 'title' => $alias_title, + 'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)), + ); + } + $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $title)); + } + } + if ($aliases) { + $data[$table][$column_real_name]['aliases'] = $aliases; + $data[$table][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known))); + } + + $data[$table][$column_real_name]['argument'] = array( + 'field' => $column_real_name, + 'table' => $table, + 'id' => $argument, + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + 'empty field name' => t('- No value -'), + ); + $data[$table][$column_real_name]['filter'] = array( + 'field' => $column_real_name, + 'table' => $table, + 'id' => $filter, + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + 'allow empty' => TRUE, + ); + if (!empty($allow_sort)) { + $data[$table][$column_real_name]['sort'] = array( + 'field' => $column_real_name, + 'table' => $table, + 'id' => $sort, + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + ); + } + + // Expose additional delta column for multiple value fields. + if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) { + $title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field['field_name'])); + $title_short_delta = t('@label:delta', array('@label' => $label)); + + $data[$table]['delta'] = array( + 'group' => $group, + 'title' => $title_delta, + 'title short' => $title_short_delta, + 'help' => t('Delta - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))), + ); + $data[$table]['delta']['field'] = array( + 'id' => 'numeric', + ); + $data[$table]['delta']['argument'] = array( + 'field' => 'delta', + 'table' => $table, + 'id' => 'numeric', + 'additional fields' => $additional_fields, + 'empty field name' => t('- No value -'), + 'field_name' => $field['field_name'], + ); + $data[$table]['delta']['filter'] = array( + 'field' => 'delta', + 'table' => $table, + 'id' => 'numeric', + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + 'allow empty' => TRUE, + ); + $data[$table]['delta']['sort'] = array( + 'field' => 'delta', + 'table' => $table, + 'id' => 'standard', + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + ); + } + + // Expose additional language column for translatable fields. + if (!empty($field['translatable'])) { + $title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field['field_name'])); + $title_short_language = t('@label:language', array('@label' => $label)); + + $data[$table]['language'] = array( + 'group' => $group, + 'title' => $title_language, + 'title short' => $title_short_language, + 'help' => t('Language - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))), + ); + $data[$table]['language']['field'] = array( + 'id' => 'language', + ); + $data[$table]['language']['argument'] = array( + 'field' => 'language', + 'table' => $table, + 'id' => 'language', + 'additional fields' => $additional_fields, + 'empty field name' => t(''), + 'field_name' => $field['field_name'], + ); + $data[$table]['language']['filter'] = array( + 'field' => 'language', + 'table' => $table, + 'id' => 'language', + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + 'allow empty' => TRUE, + ); + $data[$table]['language']['sort'] = array( + 'field' => 'language', + 'table' => $table, + 'id' => 'standard', + 'additional fields' => $additional_fields, + 'field_name' => $field['field_name'], + ); + } + } + } + + return $data; +} + +/** + * Have a different filter handler for lists. This should allow to select values of the list. + */ +function list_field_views_data($field) { + $data = field_views_field_default_views_data($field); + foreach ($data as $table_name => $table_data) { + foreach ($table_data as $field_name => $field_data) { + if (isset($field_data['filter']) && $field_name != 'delta') { + $data[$table_name][$field_name]['filter']['id'] = 'field_list'; + } + if (isset($field_data['argument']) && $field_name != 'delta') { + if ($field['type'] == 'list_text') { + $data[$table_name][$field_name]['argument']['id'] = 'field_list_string'; + } + else { + $data[$table_name][$field_name]['argument']['id'] = 'field_list'; + } + } + } + } + return $data; +} diff --git a/core/modules/views/modules/file.views.inc b/core/modules/views/modules/file.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..387b344ea33c736091e1bcf2443761d9d2928670 --- /dev/null +++ b/core/modules/views/modules/file.views.inc @@ -0,0 +1,532 @@ + 'fid', + 'title' => t('File'), + 'help' => t("Files maintained by Drupal and various modules."), + 'defaults' => array( + 'field' => 'filename' + ), + ); + $data['file_managed']['table']['entity type'] = 'file'; + + // fid + $data['file_managed']['fid'] = array( + 'title' => t('File ID'), + 'help' => t('The ID of the file.'), + 'field' => array( + 'id' => 'file', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'file_fid', + 'name field' => 'filename', // the field to display in the summary. + 'numeric' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // filename + $data['file_managed']['filename'] = array( + 'title' => t('Name'), + 'help' => t('The name of the file.'), + 'field' => array( + 'id' => 'file', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // uri + $data['file_managed']['uri'] = array( + 'title' => t('Path'), + 'help' => t('The path of the file.'), + 'field' => array( + 'id' => 'file_uri', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // filemime + $data['file_managed']['filemime'] = array( + 'title' => t('Mime type'), + 'help' => t('The mime type of the file.'), + 'field' => array( + 'id' => 'file_filemime', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // extension + $data['file_managed']['extension'] = array( + 'title' => t('Extension'), + 'help' => t('The extension of the file.'), + 'real field' => 'filename', + 'field' => array( + 'id' => 'file_extension', + 'click sortable' => FALSE, + ), + ); + + // filesize + $data['file_managed']['filesize'] = array( + 'title' => t('Size'), + 'help' => t('The size of the file.'), + 'field' => array( + 'id' => 'file_size', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'numeric', + ), + ); + + // status + $data['file_managed']['status'] = array( + 'title' => t('Status'), + 'help' => t('The status of the file.'), + 'field' => array( + 'id' => 'file_status', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'file_status', + ), + ); + + // timestamp field + $data['file_managed']['timestamp'] = array( + 'title' => t('Upload date'), + 'help' => t('The date the file was uploaded.'), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // uid + $data['file_managed']['uid'] = array( + 'title' => t('User who uploaded'), + 'help' => t('The user that uploaded the file.'), + 'relationship' => array( + 'title' => t('User who uploaded'), + 'label' => t('User who uploaded'), + 'base' => 'users', + 'base field' => 'uid', + ), + ); + + // file_usage table + + $data['file_usage']['table']['group'] = t('File Usage'); + + // Provide field-type-things to several base tables; on the core files table ("file_managed") so + // that we can create relationships from files to entities, and then on each core entity type base + // table so that we can provide general relationships between entities and files. + $data['file_usage']['table']['join'] = array( + // Link ourself to the {file_managed} table so we can provide file->entity relationships. + 'file_managed' => array( + 'field' => 'fid', + 'left_field' => 'fid', + ), + // Link ourself to the {node} table so we can provide node->file relationships. + 'node' => array( + 'field' => 'id', + 'left_field' => 'nid', + 'extra' => array(array('field' => 'type', 'value' => 'node')), + ), + // Link ourself to the {users} table so we can provide user->file relationships. + 'users' => array( + 'field' => 'id', + 'left_field' => 'uid', + 'extra' => array(array('field' => 'type', 'value' => 'user')), + ), + // Link ourself to the {comment} table so we can provide comment->file relationships. + 'comment' => array( + 'field' => 'id', + 'left_field' => 'cid', + 'extra' => array(array('field' => 'type', 'value' => 'comment')), + ), + // Link ourself to the {taxonomy_term_data} table so we can provide taxonomy_term->file relationships. + 'taxonomy_term_data' => array( + 'field' => 'id', + 'left_field' => 'tid', + 'extra' => array(array('field' => 'type', 'value' => 'taxonomy_term')), + ), + // Link ourself to the {taxonomy_vocabulary} table so we can provide taxonomy_vocabulary->file relationships. + 'taxonomy_vocabulary' => array( + 'field' => 'id', + 'left_field' => 'vid', + 'extra' => array(array('field' => 'type', 'value' => 'taxonomy_vocabulary')), + ), + ); + + // Provide a relationship between the files table and each entity type, and between each entity + // type and the files table. Entity->file relationships are type-restricted in the joins + // declared above, and file->entity relationships are type-restricted in the relationship + // declarations below. + + // Relationships between files and nodes. + $data['file_usage']['file_to_node'] = array( + 'title' => t('Content'), + 'help' => t('Content that is associated with this file, usually because this file is in a field on the content.'), + // Only provide this field/relationship/etc. when the 'file_managed' base table is present. + 'skip base' => array('node', 'node_revision', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'id', + 'relationship' => array( + 'title' => t('Content'), + 'label' => t('Content'), + 'base' => 'node', + 'base field' => 'nid', + 'relationship field' => 'id', + 'extra' => array(array('table' => 'file_usage', 'field' => 'type', 'operator' => '=', 'value' => 'node')), + ), + ); + $data['file_usage']['node_to_file'] = array( + 'title' => t('File'), + 'help' => t('A file that is associated with this node, usually because it is in a field on the node.'), + // Only provide this field/relationship/etc. when the 'node' base table is present. + 'skip base' => array('file_managed', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'fid', + 'relationship' => array( + 'title' => t('File'), + 'label' => t('File'), + 'base' => 'file_managed', + 'base field' => 'fid', + 'relationship field' => 'fid', + ), + ); + + // Relationships between files and users. + $data['file_usage']['file_to_user'] = array( + 'title' => t('User'), + 'help' => t('A user that is associated with this file, usually because this file is in a field on the user.'), + // Only provide this field/relationship/etc. when the 'file_managed' base table is present. + 'skip base' => array('node', 'node_revision', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'id', + 'relationship' => array( + 'title' => t('User'), + 'label' => t('User'), + 'base' => 'users', + 'base field' => 'uid', + 'relationship field' => 'id', + 'extra' => array(array('table' => 'file_usage', 'field' => 'type', 'operator' => '=', 'value' => 'user')), + ), + ); + $data['file_usage']['user_to_file'] = array( + 'title' => t('File'), + 'help' => t('A file that is associated with this user, usually because it is in a field on the user.'), + // Only provide this field/relationship/etc. when the 'users' base table is present. + 'skip base' => array('file_managed', 'node', 'node_revision', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'fid', + 'relationship' => array( + 'title' => t('File'), + 'label' => t('File'), + 'base' => 'file_managed', + 'base field' => 'fid', + 'relationship field' => 'fid', + ), + ); + + // Relationships between files and comments. + $data['file_usage']['file_to_comment'] = array( + 'title' => t('Comment'), + 'help' => t('A comment that is associated with this file, usually because this file is in a field on the comment.'), + // Only provide this field/relationship/etc. when the 'file_managed' base table is present. + 'skip base' => array('node', 'node_revision', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'id', + 'relationship' => array( + 'title' => t('Comment'), + 'label' => t('Comment'), + 'base' => 'comment', + 'base field' => 'cid', + 'relationship field' => 'id', + 'extra' => array(array('table' => 'file_usage', 'field' => 'type', 'operator' => '=', 'value' => 'comment')), + ), + ); + $data['file_usage']['comment_to_file'] = array( + 'title' => t('File'), + 'help' => t('A file that is associated with this comment, usually because it is in a field on the comment.'), + // Only provide this field/relationship/etc. when the 'comment' base table is present. + 'skip base' => array('file_managed', 'node', 'node_revision', 'users', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'fid', + 'relationship' => array( + 'title' => t('File'), + 'label' => t('File'), + 'base' => 'file_managed', + 'base field' => 'fid', + 'relationship field' => 'fid', + ), + ); + + // Relationships between files and taxonomy_terms. + $data['file_usage']['file_to_taxonomy_term'] = array( + 'title' => t('Taxonomy Term'), + 'help' => t('A taxonomy term that is associated with this file, usually because this file is in a field on the taxonomy term.'), + // Only provide this field/relationship/etc. when the 'file_managed' base table is present. + 'skip base' => array('node', 'node_revision', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'id', + 'relationship' => array( + 'title' => t('Taxonomy Term'), + 'label' => t('Taxonomy Term'), + 'base' => 'taxonomy_term_data', + 'base field' => 'tid', + 'relationship field' => 'id', + 'extra' => array(array('table' => 'file_usage', 'field' => 'type', 'operator' => '=', 'value' => 'taxonomy_term')), + ), + ); + $data['file_usage']['taxonomy_term_to_file'] = array( + 'title' => t('File'), + 'help' => t('A file that is associated with this taxonomy term, usually because it is in a field on the taxonomy term.'), + // Only provide this field/relationship/etc. when the 'taxonomy_term_data' base table is present. + 'skip base' => array('file_managed', 'node', 'node_revision', 'users', 'comment', 'taxonomy_vocabulary'), + 'real field' => 'fid', + 'relationship' => array( + 'title' => t('File'), + 'label' => t('File'), + 'base' => 'file_managed', + 'base field' => 'fid', + 'relationship field' => 'fid', + ), + ); + + // Relationships between files and taxonomy_vocabulary items. + $data['file_usage']['file_to_taxonomy_vocabulary'] = array( + 'title' => t('Taxonomy Vocabulary'), + 'help' => t('A taxonomy vocabulary that is associated with this file, usually because this file is in a field on the taxonomy vocabulary.'), + // Only provide this field/relationship/etc. when the 'file_managed' base table is present. + 'skip base' => array('node', 'node_revision', 'users', 'comment', 'taxonomy_term_data', 'taxonomy_vocabulary'), + 'real field' => 'id', + 'relationship' => array( + 'title' => t('Taxonomy Vocabulary'), + 'label' => t('Taxonomy Vocabulary'), + 'base' => 'taxonomy_vocabulary', + 'base field' => 'vid', + 'relationship field' => 'id', + 'extra' => array(array('table' => 'file_usage', 'field' => 'type', 'operator' => '=', 'value' => 'taxonomy_vocabulary')), + ), + ); + $data['file_usage']['taxonomy_vocabulary_to_file'] = array( + 'title' => t('File'), + 'help' => t('A file that is associated with this taxonomy vocabulary, usually because it is in a field on the taxonomy vocabulary.'), + // Only provide this field/relationship/etc. when the 'taxonomy_vocabulary' base table is present. + 'skip base' => array('file_managed', 'node', 'node_revision', 'users', 'comment', 'taxonomy_term_data'), + 'real field' => 'fid', + 'relationship' => array( + 'title' => t('File'), + 'label' => t('File'), + 'base' => 'file_managed', + 'base field' => 'fid', + 'relationship field' => 'fid', + ), + ); + + // Provide basic fields from the {file_usage} table to all of the base tables we've declared + // joins to (because there is no 'skip base' property on these fields). + $data['file_usage']['module'] = array( + 'title' => t('Module'), + 'help' => t('The module managing this file relationship.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['file_usage']['type'] = array( + 'title' => t('Entity type'), + 'help' => t('The type of entity that is related to the file.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['file_usage']['id'] = array( + 'title' => t('Entity ID'), + 'help' => t('The ID of the entity that is related to the file.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['file_usage']['count'] = array( + 'title' => t('Use count'), + 'help' => t('The number of times the file is used by this entity.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + return $data; +} + +/** + * Implements hook_field_views_data(). + * + * Views integration for file fields. Adds a file relationship to the default + * field data. + * + * @see field_views_field_default_views_data() + */ +function file_field_views_data($field) { + $data = field_views_field_default_views_data($field); + foreach ($data as $table_name => $table_data) { + // Add the relationship only on the fid field. + $data[$table_name][$field['field_name'] . '_fid']['relationship'] = array( + 'id' => 'standard', + 'base' => 'file_managed', + 'entity type' => 'file', + 'base field' => 'fid', + 'label' => t('file from !field_name', array('!field_name' => $field['field_name'])), + ); + } + + return $data; +} + +/** + * Implements hook_field_views_data_views_data_alter(). + * + * Views integration to provide reverse relationships on file fields. + */ +function file_field_views_data_views_data_alter(&$data, $field) { + foreach ($field['bundles'] as $entity_type => $bundles) { + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; + + list($label, $all_labels) = field_views_field_label($field['field_name']); + $entity = $entity_info['label']; + if ($entity == t('Node')) { + $entity = t('Content'); + } + + $data['file_managed'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'field table' => _field_sql_storage_tablename($field), + 'field field' => $field['field_name'] . '_fid', + 'base' => $entity_info['base table'], + 'base field' => $entity_info['entity keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'entity_type', + 'value' => $entity_type, + ), + 1 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ), + ), + ); + } +} + +function _views_file_status($choice = NULL) { + $status = array( + 0 => t('Temporary'), + FILE_STATUS_PERMANENT => t('Permanent'), + ); + + if (isset($choice)) { + return isset($status[$choice]) ? $status[$choice] : t('Unknown'); + } + + return $status; +} diff --git a/core/modules/views/modules/filter.views.inc b/core/modules/views/modules/filter.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..941ae3a78d3f2124e2ebee885835aa0ef9109e5d --- /dev/null +++ b/core/modules/views/modules/filter.views.inc @@ -0,0 +1,31 @@ + array( + 'left_field' => 'format', + 'field' => 'format', + ), + 'node' => array( + 'left_table' => 'node_revision', + 'left_field' => 'format', + 'field' => 'format', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/image.views.inc b/core/modules/views/modules/image.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..43c04d69ac0fc0470be528ba9fbaea0c3abda21b --- /dev/null +++ b/core/modules/views/modules/image.views.inc @@ -0,0 +1,72 @@ + $table_data) { + // Add the relationship only on the fid field. + $data[$table_name][$field['field_name'] . '_fid']['relationship'] = array( + 'id' => 'standard', + 'base' => 'file_managed', + 'base field' => 'fid', + 'label' => t('image from !field_name', array('!field_name' => $field['field_name'])), + ); + } + + return $data; +} + +/** + * Implements hook_field_views_data_views_data_alter(). + * + * Views integration to provide reverse relationships on image fields. + */ +function image_field_views_data_views_data_alter(&$data, $field) { + foreach ($field['bundles'] as $entity_type => $bundles) { + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; + + list($label, $all_labels) = field_views_field_label($field['field_name']); + $entity = $entity_info['label']; + if ($entity == t('Node')) { + $entity = t('Content'); + } + + $data['file_managed'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'field table' => _field_sql_storage_tablename($field), + 'field field' => $field['field_name'] . '_fid', + 'base' => $entity_info['base table'], + 'base field' => $entity_info['entity keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'entity_type', + 'value' => $entity_type, + ), + 1 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ), + ), + ); + } +} diff --git a/core/modules/views/modules/language.views.inc b/core/modules/views/modules/language.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..7521d52c6ff4c895c794f11f94bb4145e87a767e --- /dev/null +++ b/core/modules/views/modules/language.views.inc @@ -0,0 +1,91 @@ + 'langcode', + 'title' => t('Language'), + 'help' => t('A language used in drupal.'), + ); + + $data['language']['langcode'] = array( + 'title' => t('Language code'), + 'help' => t("Language code, e.g. 'de' or 'en-US'."), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string' + ), + 'argument' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + $data['language']['name'] = array( + 'title' => t('Language name'), + 'help' => t("Language name, e.g. 'German' or 'English'."), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string' + ), + 'argument' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + $data['language']['direction'] = array( + 'title' => t('Direction'), + 'help' => t('Direction of language (Left-to-Right = 0, Right-to-Left = 1).'), + 'field' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric' + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + $data['language']['weight'] = array( + 'title' => t('Weight'), + 'help' => t('Weight, used in lists of languages.'), + 'field' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric' + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/locale.views.inc b/core/modules/views/modules/locale.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..42bd31644f8886cb1ed5e7a6768de251c840b980 --- /dev/null +++ b/core/modules/views/modules/locale.views.inc @@ -0,0 +1,192 @@ + 'lid', + 'title' => t('Locale source'), + 'help' => t('A source string for translation, in English or the default site language.'), + ); + + // lid + $data['locales_source']['lid'] = array( + 'title' => t('LID'), + 'help' => t('The ID of the source string.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + 'numeric' => TRUE, + 'validate type' => 'lid', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // location + $data['locales_source']['location'] = array( + 'group' => t('Locale source'), + 'title' => t('Location'), + 'help' => t('A description of the location or context of the string.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // Source field + $data['locales_source']['source'] = array( + 'group' => t('Locale source'), + 'title' => t('Source'), + 'help' => t('The full original string.'), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // Source field + $data['locales_source']['context'] = array( + 'group' => t('Locale source'), + 'title' => t('Context'), + 'help' => t('The context this string applies to.'), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // Version field + $data['locales_source']['version'] = array( + 'group' => t('Locale source'), + 'title' => t('Version'), + 'help' => t('The version of Drupal core that this string is for.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'locale_version', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['locales_source']['edit_lid'] = array( + 'group' => t('Locale source'), + 'field' => array( + 'title' => t('Edit link'), + 'help' => t('Provide a simple link to edit the translations.'), + 'id' => 'locale_link_edit', + ), + ); + + // Locales target table + + // Define the base group of this table. Fields that don't + // have a group defined will go into this field by default. + $data['locales_target']['table']['group'] = t('Locale target'); + + // Join information + $data['locales_target']['table']['join'] = array( + 'locales_source' => array( + 'left_field' => 'lid', + 'field' => 'lid', + ), + ); + + // Translation field + $data['locales_target']['translation'] = array( + 'group' => t('Locale target'), + 'title' => t('Translation'), + 'help' => t('The full translation string.'), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // Language field + $data['locales_target']['language'] = array( + 'group' => t('Locale target'), + 'title' => t('Language'), + 'help' => t('The language this translation is in.'), + 'field' => array( + 'id' => 'locale_language', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'locale_language', + ), + 'argument' => array( + 'id' => 'locale_language', + ), + ); + + $data['locales_target']['plid'] = array( + 'group' => t('Locale target'), + 'title' => t('Singular LID'), + 'help' => t('The ID of the parent translation.'), + 'field' => array( + 'id' => 'standard', + ), + ); + + // Plural + $data['locales_target']['plural'] = array( + 'group' => t('Locale target'), + 'title' => t('Plural'), + 'help' => t('Whether or not the translation is plural.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Plural'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/node.views.inc b/core/modules/views/modules/node.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..bf30614cebb565d05b2cda3fee12f5009a66f4f4 --- /dev/null +++ b/core/modules/views/modules/node.views.inc @@ -0,0 +1,767 @@ + 'nid', + 'title' => t('Content'), + 'weight' => -10, + 'access query tag' => 'node_access', + 'defaults' => array( + 'field' => 'title', + ), + ); + $data['node']['table']['entity type'] = 'node'; + + // node table -- fields + + // nid + $data['node']['nid'] = array( + 'title' => t('Nid'), + 'help' => t('The node ID.'), // The help that appears on the UI, + // Information for displaying the nid + 'field' => array( + 'id' => 'node', + 'click sortable' => TRUE, + ), + // Information for accepting a nid as an argument + 'argument' => array( + 'id' => 'node_nid', + 'name field' => 'title', // the field to display in the summary. + 'numeric' => TRUE, + 'validate type' => 'nid', + ), + // Information for accepting a nid as a filter + 'filter' => array( + 'id' => 'numeric', + ), + // Information for sorting on a nid. + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + // This definition has more items in it than it needs to as an example. + $data['node']['title'] = array( + 'title' => t('Title'), // The item it appears as on the UI, + 'help' => t('The content title.'), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'field' => 'title', // the real field. This could be left out since it is the same. + 'group' => t('Content'), // The group it appears in on the UI. Could be left out. + 'id' => 'node', + 'click sortable' => TRUE, + 'link_to_node default' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + // Information for accepting a title as a filter + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // created field + $data['node']['created'] = array( + 'title' => t('Post date'), // The item it appears as on the UI, + 'help' => t('The date the content was posted.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // changed field + $data['node']['changed'] = array( + 'title' => t('Updated date'), // The item it appears as on the UI, + 'help' => t('The date the content was last updated.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // Content type + $data['node']['type'] = array( + 'title' => t('Type'), // The item it appears as on the UI, + 'help' => t('The content type (for example, "blog entry", "forum post", "story", etc).'), // The help that appears on the UI, + 'field' => array( + 'id' => 'node_type', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'node_type', + ), + 'argument' => array( + 'id' => 'node_type', + ), + ); + + // published status + $data['node']['status'] = array( + 'title' => t('Published'), + 'help' => t('Whether or not the content is published.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'published-notpublished' => array(t('Published'), t('Not published')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Published'), + 'type' => 'yes-no', + 'use_equal' => TRUE, // Use status = 1 instead of status <> 0 in WHERE statment + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // published status + extra + $data['node']['status_extra'] = array( + 'title' => t('Published or admin'), + 'help' => t('Filters out unpublished content if the current user cannot view it.'), + 'filter' => array( + 'field' => 'status', + 'id' => 'node_status', + 'label' => t('Published or admin'), + ), + ); + + // promote status + $data['node']['promote'] = array( + 'title' => t('Promoted to front page'), + 'help' => t('Whether or not the content is promoted to the front page.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'promoted-notpromoted' => array(t('Promoted'), t('Not promoted')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Promoted to front page'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // sticky + $data['node']['sticky'] = array( + 'title' => t('Sticky'), // The item it appears as on the UI, + 'help' => t('Whether or not the content is sticky.'), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'sticky' => array(t('Sticky'), t('Not sticky')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Sticky'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + 'help' => t('Whether or not the content is sticky. To list sticky content first, set this to descending.'), + ), + ); + + // Language field + if (module_exists('language')) { + $data['node']['langcode'] = array( + 'title' => t('Language'), + 'help' => t('The language the content is in.'), + 'field' => array( + 'id' => 'node_language', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'language', + ), + 'argument' => array( + 'id' => 'language', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + } + + // Define some fields based upon views_handler_field_entity in the entity + // table so they can be re-used with other query backends. + // @see views_handler_field_entity + + $data['views_entity_node']['table']['group'] = t('Content'); + + $data['views_entity_node']['view_node'] = array( + 'field' => array( + 'title' => t('Link'), + 'help' => t('Provide a simple link to the content.'), + 'id' => 'node_link', + ), + ); + + $data['views_entity_node']['edit_node'] = array( + 'field' => array( + 'title' => t('Edit link'), + 'help' => t('Provide a simple link to edit the content.'), + 'id' => 'node_link_edit', + ), + ); + + $data['views_entity_node']['delete_node'] = array( + 'field' => array( + 'title' => t('Delete link'), + 'help' => t('Provide a simple link to delete the content.'), + 'id' => 'node_link_delete', + ), + ); + + $data['node']['path'] = array( + 'field' => array( + 'title' => t('Path'), + 'help' => t('The aliased path to this content.'), + 'id' => 'node_path', + ), + ); + + // Bogus fields for aliasing purposes. + + $data['node']['created_fulldate'] = array( + 'title' => t('Created date'), + 'help' => t('Date in the form of CCYYMMDD.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_fulldate', + ), + ); + + $data['node']['created_year_month'] = array( + 'title' => t('Created year + month'), + 'help' => t('Date in the form of YYYYMM.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_year_month', + ), + ); + + $data['node']['created_year'] = array( + 'title' => t('Created year'), + 'help' => t('Date in the form of YYYY.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_year', + ), + ); + + $data['node']['created_month'] = array( + 'title' => t('Created month'), + 'help' => t('Date in the form of MM (01 - 12).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_month', + ), + ); + + $data['node']['created_day'] = array( + 'title' => t('Created day'), + 'help' => t('Date in the form of DD (01 - 31).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_day', + ), + ); + + $data['node']['created_week'] = array( + 'title' => t('Created week'), + 'help' => t('Date in the form of WW (01 - 53).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_week', + ), + ); + + $data['node']['changed_fulldate'] = array( + 'title' => t('Updated date'), + 'help' => t('Date in the form of CCYYMMDD.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_fulldate', + ), + ); + + $data['node']['changed_year_month'] = array( + 'title' => t('Updated year + month'), + 'help' => t('Date in the form of YYYYMM.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_year_month', + ), + ); + + $data['node']['changed_year'] = array( + 'title' => t('Updated year'), + 'help' => t('Date in the form of YYYY.'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_year', + ), + ); + + $data['node']['changed_month'] = array( + 'title' => t('Updated month'), + 'help' => t('Date in the form of MM (01 - 12).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_month', + ), + ); + + $data['node']['changed_day'] = array( + 'title' => t('Updated day'), + 'help' => t('Date in the form of DD (01 - 31).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_day', + ), + ); + + $data['node']['changed_week'] = array( + 'title' => t('Updated week'), + 'help' => t('Date in the form of WW (01 - 53).'), + 'argument' => array( + 'field' => 'changed', + 'id' => 'node_created_week', + ), + ); + + // uid field + $data['node']['uid'] = array( + 'title' => t('Author uid'), + 'help' => t('The user authoring the content. If you need more fields than the uid add the content: author relationship'), + 'relationship' => array( + 'title' => t('Author'), + 'help' => t('Relate content to the user who created it.'), + 'id' => 'standard', + 'base' => 'users', + 'field' => 'uid', + 'label' => t('author'), + ), + 'filter' => array( + 'id' => 'user_name', + ), + 'argument' => array( + 'id' => 'numeric', + ), + 'field' => array( + 'id' => 'user', + ), + ); + + $data['node']['uid_revision'] = array( + 'title' => t('User has a revision'), + 'help' => t('All nodes where a certain user has a revision'), + 'real field' => 'nid', + 'filter' => array( + 'id' => 'node_uid_revision', + ), + 'argument' => array( + 'id' => 'node_uid_revision', + ), + ); + + // Content revision table + + // Define the base group of this table. Fields that don't + // have a group defined will go into this field by default. + $data['node_revision']['table']['entity type'] = 'node'; + $data['node_revision']['table']['group'] = t('Content revision'); + + // Advertise this table as a possible base table + $data['node_revision']['table']['base'] = array( + 'field' => 'vid', + 'title' => t('Content revision'), + 'help' => t('Content revision is a history of changes to content.'), + 'defaults' => array( + 'field' => 'title', + ), + ); + + // For other base tables, explain how we join + $data['node_revision']['table']['join'] = array( + // Directly links to node table. + 'node' => array( + 'left_field' => 'vid', + 'field' => 'vid', + ), + ); + + // uid field for node revision + $data['node_revision']['uid'] = array( + 'title' => t('User'), + 'help' => t('Relate a content revision to the user who created the revision.'), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'users', + 'base field' => 'uid', + 'label' => t('revision user'), + ), + ); + + // nid + $data['node_revision']['nid'] = array( + 'title' => t('Nid'), + // The help that appears on the UI. + 'help' => t('The revision NID of the content revision.'), + // Information for displaying the nid. + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + // Information for accepting a nid as an argument. + 'argument' => array( + 'id' => 'node_nid', + 'click sortable' => TRUE, + 'numeric' => TRUE, + ), + // Information for accepting a nid as a filter. + 'filter' => array( + 'id' => 'numeric', + ), + // Information for sorting on a nid. + 'sort' => array( + 'id' => 'standard', + ), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'node', + 'base field' => 'nid', + 'title' => t('Content'), + 'label' => t('Get the actual content from a content revision.'), + ), + ); + + // vid + $data['node_revision']['vid'] = array( + 'title' => t('Vid'), + 'help' => t('The revision ID of the content revision.'), + // Information for displaying the vid + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + // Information for accepting a vid as an argument + 'argument' => array( + 'id' => 'node_vid', + 'click sortable' => TRUE, + 'numeric' => TRUE, + ), + // Information for accepting a vid as a filter + 'filter' => array( + 'id' => 'numeric', + ), + // Information for sorting on a vid. + 'sort' => array( + 'id' => 'standard', + ), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'node', + 'base field' => 'vid', + 'title' => t('Content'), + 'label' => t('Get the actual content from a content revision.'), + ), + ); + + // published status + $data['node_revision']['status'] = array( + 'title' => t('Published'), + 'help' => t('Whether or not the content is published.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'published-notpublished' => array(t('Published'), t('Not published')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Published'), + 'type' => 'yes-no', + 'use_equal' => TRUE, // Use status = 1 instead of status <> 0 in WHERE statment + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + $data['node_revision']['title'] = array( + 'title' => t('Title'), // The item it appears as on the UI, + 'help' => t('The content title.'), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'field' => 'title', // the real field + 'id' => 'node_revision', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // log field + $data['node_revision']['log'] = array( + 'title' => t('Log message'), // The item it appears as on the UI, + 'help' => t('The log message entered when the revision was created.'), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'id' => 'xss', + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // revision timestamp + // changed field + $data['node_revision']['timestamp'] = array( + 'title' => t('Updated date'), // The item it appears as on the UI, + 'help' => t('The date the node was last updated.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + $data['node_revision']['link_to_revision'] = array( + 'field' => array( + 'title' => t('Link'), + 'help' => t('Provide a simple link to the revision.'), + 'id' => 'node_revision_link', + ), + ); + + $data['node_revision']['revert_revision'] = array( + 'field' => array( + 'title' => t('Revert link'), + 'help' => t('Provide a simple link to revert to the revision.'), + 'id' => 'node_revision_link_revert', + ), + ); + + $data['node_revision']['delete_revision'] = array( + 'field' => array( + 'title' => t('Delete link'), + 'help' => t('Provide a simple link to delete the content revision.'), + 'id' => 'node_revision_link_delete', + ), + ); + + // Node access table + + // Define the base group of this table. Fields that don't + // have a group defined will go into this field by default. + $data['node_access']['table']['group'] = t('Content access'); + + // For other base tables, explain how we join + $data['node_access']['table']['join'] = array( + // Directly links to node table. + 'node' => array( + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + // nid field + $data['node_access']['nid'] = array( + 'title' => t('Access'), + 'help' => t('Filter by access.'), + 'filter' => array( + 'id' => 'node_access', + 'help' => t('Filter for content by view access. Not necessary if you are using node as your base table.'), + ), + ); + + // History table + + // We're actually defining a specific instance of the table, so let's + // alias it so that we can later add the real table for other purposes if we + // need it. + $data['history']['table']['group'] = t('Content'); + + // Explain how this table joins to others. + $data['history']['table']['join'] = array( + // Directly links to node table. + 'node' => array( + 'table' => 'history', + 'left_field' => 'nid', + 'field' => 'nid', + 'extra' => array( + array('field' => 'uid', 'value' => '***CURRENT_USER***', 'numeric' => TRUE), + ), + ), + ); + + $data['history']['timestamp'] = array( + 'title' => t('Has new content'), + 'field' => array( + 'id' => 'node_history_user_timestamp', + 'help' => t('Show a marker if the content is new or updated.'), + ), + 'filter' => array( + 'help' => t('Show only content that is new or updated.'), + 'id' => 'node_history_user_timestamp', + ), + ); + return $data; +} + +/** + * Implements hook_preprocess_node(). + */ +function node_row_node_view_preprocess_node(&$vars) { + $node = $vars['node']; + $options = $vars['view']->style_plugin->row_plugin->options; + + // Prevent the comment form from showing up if this is not a page display. + if ($vars['view_mode'] == 'full' && !$vars['view']->display_handler->hasPath()) { + $node->comment = FALSE; + } + + if (!$options['links']) { + unset($vars['content']['links']); + } + + if (!empty($options['comments']) && user_access('access comments') && $node->comment) { + $vars['content']['comments'] = comment_node_page_additions($node); + } +} + +/** + * Implements hook_views_query_substitutions(). + */ +function node_views_query_substitutions() { + return array( + '***ADMINISTER_NODES***' => intval(user_access('administer nodes')), + '***VIEW_OWN_UNPUBLISHED_NODES***' => intval(user_access('view own unpublished content')), + '***BYPASS_NODE_ACCESS***' => intval(user_access('bypass node access')), + ); +} + +/** + * Implements hook_views_analyze(). + */ +function node_views_analyze($view) { + $ret = array(); + // Check for something other than the default display: + if ($view->base_table == 'node') { + foreach ($view->displayHandlers as $id => $display) { + if (!$display->isDefaulted('access') || !$display->isDefaulted('filters')) { + // check for no access control + $access = $display->getOption('access'); + if (empty($access['type']) || $access['type'] == 'none') { + $select = db_select('role', 'r'); + $select->innerJoin('role_permission', 'p', 'r.rid = p.rid'); + $result = $select->fields('r', array('rid')) + ->fields('p', array('permission')) + ->condition('r.rid', array('anonymous', 'authenticated'), 'IN') + ->condition('p.permission', 'access content') + ->execute(); + + foreach ($result as $role) { + $role->safe = TRUE; + $roles[$role->rid] = $role; + } + if (!($roles['anonymous']->safe && $roles['authenticated']->safe)) { + $ret[] = Analyzer::formatMessage(t('Some roles lack permission to access content, but display %display has no access control.', array('%display' => $display['display_title'])), 'warning'); + } + $filters = $display->getOption('filters'); + foreach ($filters as $filter) { + if ($filter['table'] == 'node' && ($filter['field'] == 'status' || $filter['field'] == 'status_extra')) { + continue 2; + } + } + $ret[] = Analyzer::formatMessage(t('Display %display has no access control but does not contain a filter for published nodes.', array('%display' => $display['display_title'])), 'warning'); + } + } + } + } + foreach ($view->display as $id => $display) { + if ($display->getPluginId() == 'page') { + if ($display->getOption('path') == 'node/%') { + $ret[] = Analyzer::formatMessage(t('Display %display has set node/% as path. This will not produce what you want. If you want to have multiple versions of the node view, use panels.', array('%display' => $display['display_title'])), 'warning'); + } + } + } + + return $ret; +} + +/** + * Implements hook_views_wizard(). + */ +function node_views_wizard() { + // @todo: figure this piece out. + if (module_exists('statistics')) { + $plugins['node']['available_sorts']['node_counter-totalcount:DESC'] = t('Number of hits'); + } + +} diff --git a/core/modules/views/modules/poll.views.inc b/core/modules/views/modules/poll.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..b69f5060a6df8651292896a2753f0d4c4da920d7 --- /dev/null +++ b/core/modules/views/modules/poll.views.inc @@ -0,0 +1,46 @@ + array( + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + + // Fields + + // poll active status + $data['poll']['active'] = array( + 'title' => t('Active'), + 'help' => t('Whether the poll is open for voting.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Active'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/search.views.inc b/core/modules/views/modules/search.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..3208ede8c49a5a22ae113247f51d73df28c2fc48 --- /dev/null +++ b/core/modules/views/modules/search.views.inc @@ -0,0 +1,141 @@ + array( + 'left_field' => 'nid', + 'field' => 'sid', + ), + ); + + $data['search_total']['table']['join'] = array( + 'node' => array( + 'left_table' => 'search_index', + 'left_field' => 'word', + 'field' => 'word', + ), + 'users' => array( + 'left_table' => 'search_index', + 'left_field' => 'word', + 'field' => 'word', + ) + ); + + $data['search_dataset']['table']['join'] = array( + 'node' => array( + 'left_table' => 'search_index', + 'left_field' => 'sid', + 'field' => 'sid', + 'extra' => 'search_index.type = search_dataset.type', + 'type' => 'INNER', + ), + 'users' => array( + 'left_table' => 'search_index', + 'left_field' => 'sid', + 'field' => 'sid', + 'extra' => 'search_index.type = search_dataset.type', + 'type' => 'INNER', + ), + ); + + // Fields + + // score + $data['search_index']['score'] = array( + 'title' => t('Score'), + 'help' => t('The score of the search item. This will not be used if the search filter is not also present.'), + 'field' => array( + 'id' => 'search_score', + 'click sortable' => TRUE, + 'float' => TRUE, + 'no group by' => TRUE, + ), + // Information for sorting on a search score. + 'sort' => array( + 'id' => 'search_score', + 'no group by' => TRUE, + ), + ); + + // Search node links: forward links. + $data['search_node_links_from']['table']['group'] = t('Search'); + $data['search_node_links_from']['table']['join'] = array( + 'node' => array( + 'arguments' => array( + 'table' => 'search_node_links', + 'left_table' => 'node', + 'field' => 'nid', + 'left_field' => 'nid', + 'type' => 'INNER' + ), + ), + ); + $data['search_node_links_from']['sid'] = array( + 'title' => t('Links from'), + 'help' => t('Other nodes that are linked from the node.'), + 'argument' => array( + 'id' => 'node_nid', + ), + 'filter' => array( + 'id' => 'equality', + ), + ); + + // Search node links: backlinks. + $data['search_node_links_to']['table']['group'] = t('Search'); + $data['search_node_links_to']['table']['join'] = array( + 'node' => array( + 'arguments' => array( + 'table' => 'search_node_links', + 'left_table' => 'node', + 'field' => 'sid', + 'left_field' => 'nid', + 'type' => 'INNER' + ), + ), + ); + $data['search_node_links_to']['nid'] = array( + 'title' => t('Links to'), + 'help' => t('Other nodes that link to the node.'), + 'argument' => array( + 'id' => 'node_nid', + ), + 'filter' => array( + 'id' => 'equality', + ), + ); + + // search filter + $data['search_index']['keys'] = array( + 'title' => t('Search Terms'), // The item it appears as on the UI, + 'help' => t('The terms to search for.'), // The help that appears on the UI, + // Information for searching terms using the full search syntax + 'filter' => array( + 'id' => 'search', + 'no group by' => TRUE, + ), + 'argument' => array( + 'id' => 'search', + 'no group by' => TRUE, + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/statistics.views.inc b/core/modules/views/modules/statistics.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..b4e3caf1f139b890c3bd06dab0e98c2d241e99fc --- /dev/null +++ b/core/modules/views/modules/statistics.views.inc @@ -0,0 +1,259 @@ + array( + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + + // totalcount + $data['node_counter']['totalcount'] = array( + 'title' => t('Total views'), + 'help' => t('The total number of times the node has been viewed.'), + + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // daycount + $data['node_counter']['daycount'] = array( + 'title' => t('Views today'), + 'help' => t('The total number of times the node has been viewed today.'), + + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // timestamp + $data['node_counter']['timestamp'] = array( + 'title' => t('Most recent view'), + 'help' => t('The most recent time the node has been viewed.'), + + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'date', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // accesslog table + + $data['accesslog']['table']['group'] = t('Access log'); + + // Advertise this table as a possible base table + $data['accesslog']['table']['base'] = array( + 'field' => 'aid', + 'title' => t('Access log'), + 'help' => t('Stores site access information.'), + 'weight' => 10, + ); + + // For other base tables, explain how we join + $data['accesslog']['table']['join'] = array( + 'users' => array( + 'field' => 'uid', + 'left_field' => 'uid', + ), + ); + + // accesslog.aid + $data['accesslog']['aid'] = array( + 'title' => t('Aid'), + 'help' => t('Unique access event ID.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + 'name field' => 'wid', + 'numeric' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // session id + $data['accesslog']['sid'] = array( + 'title' => t('Session ID'), + 'help' => t('Browser session ID of user that visited page.'), + + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // title + $data['accesslog']['title'] = array( + 'title' => t('Page title'), + 'help' => t('Title of page visited.'), + + 'field' => array( + 'id' => 'accesslog_path', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'standard', + ), + ); + + // path + $data['accesslog']['path'] = array( + 'title' => t('Path'), + 'help' => t('Internal path to page visited (relative to Drupal root.)'), + + 'field' => array( + 'id' => 'accesslog_path', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + //No argument here. Can't send forward slashes as arguments. + //Can be worked around by node ID. + //(but what about aliases?) + ); + + // referrer + $data['accesslog']['url'] = array( + 'title' => t('Referrer'), + 'help' => t('Referrer URI.'), + 'field' => array( + 'id' => 'url', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // hostname + $data['accesslog']['hostname'] = array( + 'title' => t('Hostname'), + 'help' => t('Hostname of user that visited the page.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'string', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // user + $data['accesslog']['uid'] = array( + 'title' => t('User'), + 'help' => t('The user who visited the site.'), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'users', + 'base field' => 'uid', + ), + ); + + // timer + $data['accesslog']['timer'] = array( + 'title' => t('Timer'), + 'help' => t('Time in milliseconds that the page took to load.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // timestamp + $data['accesslog']['timestamp'] = array( + 'title' => t('Timestamp'), + 'help' => t('Timestamp of when the page was visited.'), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + return $data; +} diff --git a/core/modules/views/modules/taxonomy.views.inc b/core/modules/views/modules/taxonomy.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..e3f6e1822249e573c0e84c2c816752a69c136dbd --- /dev/null +++ b/core/modules/views/modules/taxonomy.views.inc @@ -0,0 +1,501 @@ + array( + 'left_field' => 'vid', + 'field' => 'vid', + ), + ); + + // vocabulary name + $data['taxonomy_vocabulary']['name'] = array( + 'title' => t('Name'), // The item it appears as on the UI, + 'field' => array( + 'help' => t('Name of the vocabulary a term is a member of. This will be the vocabulary that whichever term the "Taxonomy: Term" field is; and can similarly cause duplicates.'), + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + 'help' => t('The taxonomy vocabulary name'), + ), + ); + $data['taxonomy_vocabulary']['machine_name'] = array( + 'title' => t('Machine name'), // The item it appears as on the UI, + 'field' => array( + 'help' => t('Machine-Name of the vocabulary a term is a member of. This will be the vocabulary that whichever term the "Taxonomy: Term" field is; and can similarly cause duplicates.'), + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'help' => t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'), + 'id' => 'vocabulary_machine_name', + ), + 'argument' => array( + 'help' => t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'), + 'id' => 'vocabulary_machine_name', + ), + ); + $data['taxonomy_vocabulary']['vid'] = array( + 'title' => t('Vocabulary ID'), // The item it appears as on the UI, + 'help' => t('The taxonomy vocabulary ID'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'vocabulary_vid', + 'name field' => 'name', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + $data['taxonomy_vocabulary']['description'] = array( + 'title' => t('Description'), // The item it appears as on the UI, + 'help' => t('The taxonomy vocabulary description'), + 'field' => array( + 'id' => 'standard', + ), + ); + $data['taxonomy_vocabulary']['weight'] = array( + 'title' => t('Weight'), + 'help' => t('The taxonomy vocabulary weight'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + 'name field' => 'weight', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'numeric', + ), + ); + + // taxonomy_term_data table + + $data['taxonomy_term_data']['table']['group'] = t('Taxonomy term'); + $data['taxonomy_term_data']['table']['base'] = array( + 'field' => 'tid', + 'title' => t('Term'), + 'help' => t('Taxonomy terms are attached to nodes.'), + 'access query tag' => 'term_access', + ); + $data['taxonomy_term_data']['table']['entity type'] = 'taxonomy_term'; + + // The term data table + $data['taxonomy_term_data']['table']['join'] = array( + 'taxonomy_vocabulary' => array( + 'field' => 'vid', + 'left_field' => 'vid', + ), + // This is provided for many_to_one argument + 'taxonomy_index' => array( + 'field' => 'tid', + 'left_field' => 'tid', + ), + ); + + // tid field + $data['taxonomy_term_data']['tid'] = array( + 'title' => t('Term ID'), + 'help' => t('The tid of a taxonomy term.'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'taxonomy', + 'name field' => 'name', + 'zero is null' => TRUE, + ), + 'filter' => array( + 'title' => t('Term'), + 'help' => t('Taxonomy term chosen from autocomplete or select widget.'), + 'id' => 'taxonomy_index_tid', + 'hierarchy table' => 'taxonomy_term_hierarchy', + 'numeric' => TRUE, + ), + ); + + // raw tid field + $data['taxonomy_term_data']['tid_raw'] = array( + 'title' => t('Term ID'), + 'help' => t('The tid of a taxonomy term.'), + 'real field' => 'tid', + 'filter' => array( + 'id' => 'numeric', + 'allow empty' => TRUE, + ), + ); + + $data['taxonomy_term_data']['tid_representative'] = array( + 'relationship' => array( + 'title' => t('Representative node'), + 'label' => t('Representative node'), + 'help' => t('Obtains a single representative node for each term, according to a chosen sort criterion.'), + 'id' => 'groupwise_max', + 'relationship field' => 'tid', + 'outer field' => 'taxonomy_term_data.tid', + 'argument table' => 'taxonomy_term_data', + 'argument field' => 'tid', + 'base' => 'node', + 'field' => 'nid', + 'relationship' => 'node:term_node_tid' + ), + ); + + // Term name field + $data['taxonomy_term_data']['name'] = array( + 'title' => t('Name'), + 'help' => t('The taxonomy term name.'), + 'field' => array( + 'id' => 'taxonomy', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + 'help' => t('Taxonomy term name.'), + ), + 'argument' => array( + 'id' => 'string', + 'help' => t('Taxonomy term name.'), + 'many to one' => TRUE, + 'empty field name' => t('Uncategorized'), + ), + ); + + // taxonomy weight + $data['taxonomy_term_data']['weight'] = array( + 'title' => t('Weight'), + 'help' => t('The term weight field'), + 'field' => array( + 'id' => 'numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'numeric', + ), + ); + + // Term description + $data['taxonomy_term_data']['description'] = array( + 'title' => t('Term description'), + 'help' => t('The description associated with a taxonomy term.'), + 'field' => array( + 'id' => 'markup', + 'format' => array('field' => 'format'), + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + // Term vocabulary + $data['taxonomy_term_data']['vid'] = array( + 'title' => t('Vocabulary'), + 'help' => t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'), + 'filter' => array( + 'id' => 'vocabulary_vid', + ), + ); + + $data['taxonomy_term_data']['langcode'] = array( + 'title' => t('Language'), // The item it appears as on the UI, + 'help' => t('Language of the term'), + 'field' => array( + 'id' => 'taxonomy_term_language', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'language', + ), + 'argument' => array( + 'id' => 'language', + ), + ); + + // Link to edit the term + $data['taxonomy_term_data']['edit_term'] = array( + 'field' => array( + 'title' => t('Term edit link'), + 'help' => t('Provide a simple link to edit the term.'), + 'id' => 'term_link_edit', + ), + ); + + // taxonomy_index table + + $data['taxonomy_index']['table']['group'] = t('Taxonomy term'); + + $data['taxonomy_index']['table']['join'] = array( + 'taxonomy_term_data' => array( + // links directly to taxonomy_term_data via tid + 'left_field' => 'tid', + 'field' => 'tid', + ), + 'node' => array( + // links directly to node via nid + 'left_field' => 'nid', + 'field' => 'nid', + ), + 'taxonomy_term_hierarchy' => array( + 'left_field' => 'tid', + 'field' => 'tid', + ), + ); + + $data['taxonomy_index']['nid'] = array( + 'title' => t('Content with term'), + 'help' => t('Relate all content tagged with a term.'), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'node', + 'base field' => 'nid', + 'label' => t('node'), + 'skip base' => 'node', + ), + ); + + // @todo This stuff needs to move to a node field since + // really it's all about nodes. + // tid field + $data['taxonomy_index']['tid'] = array( + 'group' => t('Content'), + 'title' => t('Has taxonomy term ID'), + 'help' => t('Display content if it has the selected taxonomy terms.'), + 'argument' => array( + 'id' => 'taxonomy_index_tid', + 'name table' => 'taxonomy_term_data', + 'name field' => 'name', + 'empty field name' => t('Uncategorized'), + 'numeric' => TRUE, + 'skip base' => 'taxonomy_term_data', + ), + 'filter' => array( + 'title' => t('Has taxonomy term'), + 'id' => 'taxonomy_index_tid', + 'hierarchy table' => 'taxonomy_term_hierarchy', + 'numeric' => TRUE, + 'skip base' => 'taxonomy_term_data', + 'allow empty' => TRUE, + ), + ); + + // term_hierarchy table + + $data['taxonomy_term_hierarchy']['table']['group'] = t('Taxonomy term'); + + $data['taxonomy_term_hierarchy']['table']['join'] = array( + 'taxonomy_term_hierarchy' => array( + // links to self through left.parent = right.tid (going down in depth) + 'left_field' => 'tid', + 'field' => 'parent', + ), + 'taxonomy_term_data' => array( + // links directly to taxonomy_term_data via tid + 'left_field' => 'tid', + 'field' => 'tid', + ), + ); + + $data['taxonomy_term_hierarchy']['parent'] = array( + 'title' => t('Parent term'), + 'help' => t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'), + 'relationship' => array( + 'base' => 'taxonomy_term_data', + 'field' => 'parent', + 'label' => t('Parent'), + ), + 'filter' => array( + 'help' => t('Filter the results of "Taxonomy: Term" by the parent pid.'), + 'id' => 'numeric', + ), + 'argument' => array( + 'help' => t('The parent term of the term.'), + 'id' => 'taxonomy', + ), + ); + + return $data; +} + +/** + * Implements hook_views_data_alter(). + */ +function taxonomy_views_data_alter(&$data) { + $data['node']['term_node_tid'] = array( + 'title' => t('Taxonomy terms on node'), + 'help' => t('Relate nodes to taxonomy terms, specifiying which vocabulary or vocabularies to use. This relationship will cause duplicated records if there are multiple terms.'), + 'relationship' => array( + 'id' => 'node_term_data', + 'label' => t('term'), + 'base' => 'taxonomy_term_data', + ), + 'field' => array( + 'title' => t('All taxonomy terms'), + 'help' => t('Display all taxonomy terms associated with a node from specified vocabularies.'), + 'id' => 'taxonomy_index_tid', + 'no group by' => TRUE, + ), + ); + + $data['node']['term_node_tid_depth'] = array( + 'help' => t('Display content if it has the selected taxonomy terms, or children of the selected terms. Due to additional complexity, this has fewer options than the versions without depth.'), + 'real field' => 'nid', + 'argument' => array( + 'title' => t('Has taxonomy term ID (with depth)'), + 'id' => 'taxonomy_index_tid_depth', + 'accept depth modifier' => TRUE, + ), + 'filter' => array( + 'title' => t('Has taxonomy terms (with depth)'), + 'id' => 'taxonomy_index_tid_depth', + ), + ); + + $data['node']['term_node_tid_depth_modifier'] = array( + 'title' => t('Has taxonomy term ID depth modifier'), + 'help' => t('Allows the "depth" for Taxonomy: Term ID (with depth) to be modified via an additional contextual filter value.'), + 'argument' => array( + 'id' => 'taxonomy_index_tid_depth_modifier', + ), + ); +} + +/** + * Implements hook_field_views_data(). + * + * Views integration for taxonomy_term_reference fields. Adds a term relationship to the default + * field data. + * + * @see field_views_field_default_views_data() + */ +function taxonomy_field_views_data($field) { + $data = field_views_field_default_views_data($field); + foreach ($data as $table_name => $table_data) { + foreach ($table_data as $field_name => $field_data) { + if (isset($field_data['filter']) && $field_name != 'delta') { + $data[$table_name][$field_name]['filter']['id'] = 'taxonomy_index_tid'; + $data[$table_name][$field_name]['filter']['vocabulary'] = $field['settings']['allowed_values'][0]['vocabulary']; + } + } + + // Add the relationship only on the tid field. + $data[$table_name][$field['field_name'] . '_tid']['relationship'] = array( + 'id' => 'standard', + 'base' => 'taxonomy_term_data', + 'base field' => 'tid', + 'label' => t('term from !field_name', array('!field_name' => $field['field_name'])), + ); + + } + + return $data; +} + +/** + * Implements hook_field_views_data_views_data_alter(). + * + * Views integration to provide reverse relationships on term references. + */ +function taxonomy_field_views_data_views_data_alter(&$data, $field) { + foreach ($field['bundles'] as $entity_type => $bundles) { + $entity_info = entity_get_info($entity_type); + $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type; + + list($label, $all_labels) = field_views_field_label($field['field_name']); + $entity = $entity_info['label']; + if ($entity == t('Node')) { + $entity = t('Content'); + } + + $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array( + 'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)), + 'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity, '@field' => $label)), + 'id' => 'entity_reverse', + 'field_name' => $field['field_name'], + 'field table' => _field_sql_storage_tablename($field), + 'field field' => $field['field_name'] . '_tid', + 'base' => $entity_info['base table'], + 'base field' => $entity_info['entity keys']['id'], + 'label' => t('!field_name', array('!field_name' => $field['field_name'])), + 'join_extra' => array( + 0 => array( + 'field' => 'entity_type', + 'value' => $entity_type, + ), + 1 => array( + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ), + ), + ); + } +} + +/** + * Helper function to set a breadcrumb for taxonomy. + */ +function views_taxonomy_set_breadcrumb(&$breadcrumb, &$argument) { + if (empty($argument->options['set_breadcrumb'])) { + return; + } + + $args = $argument->view->args; + $parents = taxonomy_get_parents_all($argument->argument); + foreach (array_reverse($parents) as $parent) { + // Unfortunately parents includes the current argument. Skip. + if ($parent->tid == $argument->argument) { + continue; + } + if (!empty($argument->options['use_taxonomy_term_path'])) { + $path = taxonomy_term_uri($parent); + $path = $path['path']; + } + else { + $args[$argument->position] = $parent->tid; + $path = $argument->view->getUrl($args); + } + $breadcrumb[$path] = check_plain($parent->name); + } +} diff --git a/core/modules/views/modules/translation.views.inc b/core/modules/views/modules/translation.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..6564b31829dca0b70ebf229295b4a84b5f1c200c --- /dev/null +++ b/core/modules/views/modules/translation.views.inc @@ -0,0 +1,120 @@ + 'tnid', + 'field' => 'tnid', + ); + + // The translation ID (nid of the "source" translation) + $data['node']['tnid'] = array( + 'group' => t('Content translation'), + 'title' => t('Translation set node ID'), + 'help' => t('The ID of the translation set the content belongs to.'), + 'field' => array( + 'id' => 'node', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'numeric', + ), + 'argument' => array( + 'id' => 'node_tnid', + 'name field' => 'title', // the field to display in the summary. + 'numeric' => TRUE, + 'validate type' => 'tnid', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'relationship' => array( + 'title' => t('Source translation'), + 'help' => t('The source that this content was translated from.'), + 'base' => 'node', + 'base field' => 'nid', + 'id' => 'standard', + 'label' => t('Source translation'), + ), + ); + + // The source translation. + $data['node']['translation'] = array( + 'group' => t('Content translation'), + 'title' => t('Translations'), + 'help' => t('Versions of content in different languages.'), + 'relationship' => array( + 'title' => t('Translations'), + 'help' => t('Versions of content in different languages.'), + 'base' => 'node', + 'base field' => 'tnid', + 'relationship table' => 'node', + 'relationship field' => 'nid', + 'id' => 'translation', + 'label' => t('Translations'), + ), + ); + + // The source translation. + $data['node']['source_translation'] = array( + 'group' => t('Content translation'), + 'title' => t('Source translation'), + 'help' => t('Content that is either untranslated or is the original version of a translation set.'), + 'filter' => array( + 'id' => 'node_tnid', + ), + ); + + // The source translation. + $data['node']['child_translation'] = array( + 'group' => t('Node translation'), + 'title' => t('Child translation'), + 'help' => t('Content that is a translation of a source translation.'), + 'filter' => array( + 'id' => 'node_tnid_child', + ), + ); + + // Translation status + $data['node']['translate'] = array( + 'group' => t('Content translation'), + 'title' => t('Translation status'), + 'help' => t('The translation status of the content - whether or not the translation needs to be updated.'), + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Outdated'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // Translate node link. + $data['node']['translate_node'] = array( + 'group' => t('Content translation'), + 'title' => t('Translate link'), + 'help' => t('Provide a simple link to translate the node.'), + 'field' => array( + 'id' => 'node_link_translate', + ), + ); + +} diff --git a/core/modules/views/modules/user.views.inc b/core/modules/views/modules/user.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..295278a2ecbcb9b6cfcd82c6631f5b80acd6f8fd --- /dev/null +++ b/core/modules/views/modules/user.views.inc @@ -0,0 +1,481 @@ + 'uid', + 'title' => t('User'), + 'help' => t('Users who have created accounts on your site.'), + 'access query tag' => 'user_access', + ); + $data['users']['table']['entity type'] = 'user'; + + // uid + $data['users']['uid'] = array( + 'title' => t('Uid'), + 'help' => t('The user ID'), // The help that appears on the UI, + 'field' => array( + 'id' => 'user', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'id' => 'user_uid', + 'name field' => 'name', // display this field in the summary + ), + 'filter' => array( + 'title' => t('Name'), + 'id' => 'user_name', + ), + 'sort' => array( + 'id' => 'standard', + ), + 'relationship' => array( + 'title' => t('Content authored'), + 'help' => t('Relate content to the user who created it. This relationship will create one record for each content item created by the user.'), + 'id' => 'standard', + 'base' => 'node', + 'base field' => 'uid', + 'field' => 'uid', + 'label' => t('nodes'), + ), + ); + + // uid_raw + $data['users']['uid_raw'] = array( + 'help' => t('The raw numeric user ID.'), + 'real field' => 'uid', + 'filter' => array( + 'title' => t('The user ID'), + 'id' => 'numeric', + ), + ); + + // uid + $data['users']['uid_representative'] = array( + 'relationship' => array( + 'title' => t('Representative node'), + 'label' => t('Representative node'), + 'help' => t('Obtains a single representative node for each user, according to a chosen sort criterion.'), + 'id' => 'groupwise_max', + 'relationship field' => 'uid', + 'outer field' => 'users.uid', + 'argument table' => 'users', + 'argument field' => 'uid', + 'base' => 'node', + 'field' => 'nid', + 'relationship' => 'node:uid' + ), + ); + + // uid + $data['users']['uid_current'] = array( + 'real field' => 'uid', + 'title' => t('Current'), + 'help' => t('Filter the view to the currently logged in user.'), + 'filter' => array( + 'id' => 'user_current', + 'type' => 'yes-no', + ), + ); + + // name + $data['users']['name'] = array( + 'title' => t('Name'), // The item it appears as on the UI, + 'help' => t('The user or author name.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'user_name', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'argument' => array( + 'id' => 'string', + ), + 'filter' => array( + 'id' => 'string', + 'title' => t('Name (raw)'), + 'help' => t('The user or author name. This filter does not check if the user exists and allows partial matching. Does not utilize autocomplete.') + ), + ); + + // mail + // Note that this field implements field level access control. + $data['users']['mail'] = array( + 'title' => t('E-mail'), // The item it appears as on the UI, + 'help' => t('Email address for a given user. This field is normally not shown to users, so be cautious when using it.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'user_mail', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + // language + $data['users']['langcode'] = array( + 'title' => t('Language'), // The item it appears as on the UI, + 'help' => t('Language of the user'), + 'field' => array( + 'id' => 'user_language', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'language', + ), + 'argument' => array( + 'id' => 'language', + ), + ); + + // picture + $data['users']['picture'] = array( + 'title' => t('Picture'), + 'help' => t("The user's picture, if allowed."), // The help that appears on the UI, + // Information for displaying the uid + 'field' => array( + 'id' => 'user_picture', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Has Avatar'), + 'type' => 'yes-no', + ), + ); + + // link + $data['users']['view_user'] = array( + 'field' => array( + 'title' => t('Link'), + 'help' => t('Provide a simple link to the user.'), + 'id' => 'user_link', + ), + ); + + // created field + $data['users']['created'] = array( + 'title' => t('Created date'), // The item it appears as on the UI, + 'help' => t('The date the user was created.'), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + $data['users']['created_fulldate'] = array( + 'title' => t('Created date'), + 'help' => t('Date in the form of CCYYMMDD.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_fulldate', + ), + ); + + $data['users']['created_year_month'] = array( + 'title' => t('Created year + month'), + 'help' => t('Date in the form of YYYYMM.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_year_month', + ), + ); + + $data['users']['created_year'] = array( + 'title' => t('Created year'), + 'help' => t('Date in the form of YYYY.'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_year', + ), + ); + + $data['users']['created_month'] = array( + 'title' => t('Created month'), + 'help' => t('Date in the form of MM (01 - 12).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_month', + ), + ); + + $data['users']['created_day'] = array( + 'title' => t('Created day'), + 'help' => t('Date in the form of DD (01 - 31).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_day', + ), + ); + + $data['users']['created_week'] = array( + 'title' => t('Created week'), + 'help' => t('Date in the form of WW (01 - 53).'), + 'argument' => array( + 'field' => 'created', + 'id' => 'node_created_week', + ), + ); + + // access field + $data['users']['access'] = array( + 'title' => t('Last access'), // The item it appears as on the UI, + 'help' => t("The user's last access date."), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // login field + $data['users']['login'] = array( + 'title' => t('Last login'), // The item it appears as on the UI, + 'help' => t("The user's last login date."), // The help that appears on the UI, + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date' + ), + 'filter' => array( + 'id' => 'date', + ), + ); + + // active status + $data['users']['status'] = array( + 'title' => t('Active'), // The item it appears as on the UI, + 'help' => t('Whether a user is active or blocked.'), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'id' => 'boolean', + 'click sortable' => TRUE, + 'output formats' => array( + 'active-blocked' => array(t('Active'), t('Blocked')), + ), + ), + 'filter' => array( + 'id' => 'boolean', + 'label' => t('Active'), + 'type' => 'yes-no', + ), + 'sort' => array( + 'id' => 'standard', + ), + ); + + // log field + $data['users']['signature'] = array( + 'title' => t('Signature'), // The item it appears as on the UI, + 'help' => t("The user's signature."), // The help that appears on the UI, + // Information for displaying a title as a field + 'field' => array( + 'id' => 'markup', + 'format' => filter_fallback_format(), + ), + 'filter' => array( + 'id' => 'string', + ), + ); + + $data['users']['edit_node'] = array( + 'field' => array( + 'title' => t('Edit link'), + 'help' => t('Provide a simple link to edit the user.'), + 'id' => 'user_link_edit', + ), + ); + + $data['users']['cancel_node'] = array( + 'field' => array( + 'title' => t('Cancel link'), + 'help' => t('Provide a simple link to cancel the user.'), + 'id' => 'user_link_cancel', + ), + ); + + $data['users']['data'] = array( + 'title' => t('Data'), + 'help' => t('Provide serialized data of the user'), + 'field' => array( + 'id' => 'serialized', + ), + ); + + // users_roles table + + $data['users_roles']['table']['group'] = t('User'); + + // Explain how this table joins to others. + $data['users_roles']['table']['join'] = array( + // Directly links to users table. + 'users' => array( + 'left_field' => 'uid', + 'field' => 'uid', + ), + ); + + $data['users_roles']['rid'] = array( + 'title' => t('Roles'), + 'help' => t('Roles that a user belongs to.'), + 'field' => array( + 'id' => 'user_roles', + 'no group by' => TRUE, + ), + 'filter' => array( + 'id' => 'user_roles', + 'allow empty' => TRUE, + ), + 'argument' => array( + 'id' => 'users_roles_rid', + 'name table' => 'role', + 'name field' => 'name', + 'empty field name' => t('No role'), + 'zero is null' => TRUE, + 'numeric' => TRUE, + ), + ); + + // role table + + $data['role']['table']['join'] = array( + // Directly links to users table. + 'users' => array( + 'left_table' => 'users_roles', + 'left_field' => 'rid', + 'field' => 'rid', + ), + // needed for many to one helper sometimes + 'users_roles' => array( + 'left_field' => 'rid', + 'field' => 'rid', + ), + ); + + // permission table + $data['role_permission']['table']['group'] = t('User'); + $data['role_permission']['table']['join'] = array( + // Directly links to users table. + 'users' => array( + 'left_table' => 'users_roles', + 'left_field' => 'rid', + 'field' => 'rid', + ), + ); + + $data['role_permission']['permission'] = array( + 'title' => t('Permission'), + 'help' => t('The user permissions.'), + 'field' => array( + 'id' => 'user_permissions', + 'no group by' => TRUE, + ), + 'filter' => array( + 'id' => 'user_permissions', + ), + ); + + // authmap table + + $data['authmap']['table']['group'] = t('User'); + $data['authmap']['table']['join'] = array( + // Directly links to users table. + 'users' => array( + 'left_field' => 'uid', + 'field' => 'uid', + ), + ); + + $data['authmap']['aid'] = array( + 'title' => t('Authmap ID'), + 'help' => t('The Authmap ID.'), + 'field' => array( + 'id' => 'numeric', + ), + 'filter' => array( + 'id' => 'numeric', + 'numeric' => TRUE, + ), + 'argument' => array( + 'id' => 'numeric', + 'numeric' => TRUE, + ), + ); + $data['authmap']['authname'] = array( + 'title' => t('Authentication name'), + 'help' => t('The unique authentication name.'), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'numeric', + ), + ); + $data['authmap']['module'] = array( + 'title' => t('Authentication module'), + 'help' => t('The name of the module managing the authentication entry.'), + 'field' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + return $data; +} + +/** + * Allow replacement of current userid so we can cache these queries + */ +function user_views_query_substitutions($view) { + global $user; + return array('***CURRENT_USER***' => intval($user->uid)); +} diff --git a/core/modules/views/modules/views.views.inc b/core/modules/views/modules/views.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..069dd99150c3db237a2d209fc78928d18050c581 --- /dev/null +++ b/core/modules/views/modules/views.views.inc @@ -0,0 +1,112 @@ + array(), + ); + + $data['views']['random'] = array( + 'title' => t('Random'), + 'help' => t('Randomize the display order.'), + 'sort' => array( + 'id' => 'random', + ), + ); + + $data['views']['null'] = array( + 'title' => t('Null'), + 'help' => t('Allow a contextual filter value to be ignored. The query will not be altered by this contextual filter value. Can be used when contextual filter values come from the URL, and a part of the URL needs to be ignored.'), + 'argument' => array( + 'id' => 'null', + ), + ); + + $data['views']['nothing'] = array( + 'title' => t('Custom text'), + 'help' => t('Provide custom text or link.'), + 'field' => array( + 'id' => 'custom', + ), + ); + + $data['views']['counter'] = array( + 'title' => t('View result counter'), + 'help' => t('Displays the actual position of the view result'), + 'field' => array( + 'id' => 'counter', + ), + ); + + $data['views']['area'] = array( + 'title' => t('Text area'), + 'help' => t('Provide markup text for the area.'), + 'area' => array( + 'id' => 'text', + ), + ); + + $data['views']['area_text_custom'] = array( + 'title' => t('Unfiltered text'), + 'help' => t('Add unrestricted, custom text or markup. This is similar to the custom text field.'), + 'area' => array( + 'id' => 'text_custom', + ), + ); + + $data['views']['title'] = array( + 'title' => t('Title override'), + 'help' => t('Override the default view title for this view. This is useful to display an alternative title when a view is empty.'), + 'area' => array( + 'id' => 'title', + 'sub_type' => 'empty', + ), + ); + + $data['views']['view'] = array( + 'title' => t('View area'), + 'help' => t('Insert a view inside an area.'), + 'area' => array( + 'id' => 'view', + ), + ); + + $data['views']['result'] = array( + 'title' => t('Result summary'), + 'help' => t('Shows result summary, for example the items per page.'), + 'area' => array( + 'id' => 'result', + ), + ); + + if (module_exists('contextual')) { + $data['views']['contextual_links'] = array( + 'title' => t('Contextual Links'), + 'help' => t('Display fields in a contextual links menu.'), + 'field' => array( + 'id' => 'contextual_links', + ), + ); + } + + $data['views']['combine'] = array( + 'title' => t('Combine fields filter'), + 'help' => t('Combine two fields together and search by them.'), + 'filter' => array( + 'id' => 'combine', + ), + ); + + return $data; +} diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_access_none.yml b/core/modules/views/tests/views_test_config/config/views.view.test_access_none.yml new file mode 100644 index 0000000000000000000000000000000000000000..79019ef9f79c59a3e092fea1254c173a45e7f71c --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_access_none.yml @@ -0,0 +1,27 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_access_none +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_access_perm.yml b/core/modules/views/tests/views_test_config/config/views.view.test_access_perm.yml new file mode 100644 index 0000000000000000000000000000000000000000..f509323b434483bae2d11ad8e7a0f6f7fbb8adc8 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_access_perm.yml @@ -0,0 +1,29 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + options: + perm: 'views_test_data test permission' + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_access_perm +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_access_role.yml b/core/modules/views/tests/views_test_config/config/views.view.test_access_role.yml new file mode 100644 index 0000000000000000000000000000000000000000..3d2b4d98fd46b20914aa65a427b1c9979ee3541f --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_access_role.yml @@ -0,0 +1,27 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: role + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_access_role +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_aggregate_count.yml b/core/modules/views/tests/views_test_config/config/views.view.test_aggregate_count.yml new file mode 100644 index 0000000000000000000000000000000000000000..7a4224790a48a61728f056aafc6921d45e744e21 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_aggregate_count.yml @@ -0,0 +1,57 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + type: + default_action: summary + default_argument_type: fixed + field: type + id: type + summary: + format: default_summary + table: node + cache: + type: none + exposed_form: + type: basic + fields: + nid: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: title + hide_empty: '0' + id: nid + link_to_node: '0' + table: node + group_by: '1' + pager: + type: some + query: + options: + query_comment: '0' + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_aggregate_count +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_alias.yml b/core/modules/views/tests/views_test_config/config/views.view.test_alias.yml new file mode 100644 index 0000000000000000000000000000000000000000..3755f24f3c8704230bff5bab0d7a1abd06cd749d --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_alias.yml @@ -0,0 +1,88 @@ +api_version: '3.0' +base_table: users +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + perm: 'access user profiles' + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + name: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '0' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '0' + empty_zero: '0' + field: name + hide_empty: '0' + id: name + label: '' + link_to_user: '1' + overwrite_anonymous: '0' + table: users + filters: + uid_raw: + admin_label: '' + expose: + description: '' + identifier: '' + label: '' + multiple: '0' + operator: '' + operator_id: '0' + remember: '0' + remember_roles: + authenticated: authenticated + required: '0' + use_operator: '0' + exposed: '0' + field: uid_raw + group: '1' + group_info: + default_group: All + default_group_multiple: { } + description: '' + group_items: { } + identifier: '' + label: '' + multiple: '0' + optional: '1' + remember: '0' + widget: select + group_type: group + id: uid_raw + is_grouped: '0' + operator: '>' + relationship: none + table: users + value: + max: '' + min: '' + value: '1' + pager: + type: full + query: + type: views_query + row_plugin: fields + style_plugin: default + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_alias +module: views +name: test_alias +tag: default +uuid: 3bdfd3e6-15aa-4324-9005-5ad8b321d265 diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_argument_default_fixed.yml b/core/modules/views/tests/views_test_config/config/views.view.test_argument_default_fixed.yml new file mode 100644 index 0000000000000000000000000000000000000000..061b2097c627eb91a0b8102f42376015f7a63503 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_argument_default_fixed.yml @@ -0,0 +1,56 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + 'null': + default_action: default + default_argument_type: fixed + field: 'null' + id: 'null' + must_not_be: '0' + style_plugin: default_summary + table: views + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: title + hide_empty: '0' + id: title + link_to_node: '0' + table: node + pager: + options: + id: '0' + items_per_page: '10' + offset: '0' + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_argument_default_fixed +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_comment_user_uid.yml b/core/modules/views/tests/views_test_config/config/views.view.test_comment_user_uid.yml new file mode 100644 index 0000000000000000000000000000000000000000..4dca1cd32d7a30d68ac627898769ade1de50cbf1 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_comment_user_uid.yml @@ -0,0 +1,48 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + arguments: + uid_touch: + default_argument_skip_url: '0' + default_argument_type: fixed + field: uid_touch + id: uid_touch + summary: + format: default_summary + number_of_records: '0' + summary_options: + items_per_page: '25' + table: node + cache: + type: none + exposed_form: + type: basic + fields: + nid: + field: nid + id: nid + table: node + pager: + type: full + query: + options: + query_comment: '0' + type: views_query + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_comment_user_uid +name: test_comment_user_uid +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml b/core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml new file mode 100644 index 0000000000000000000000000000000000000000..a28c6b11550143644e0b8a7420fd20c9cefae2ce --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml @@ -0,0 +1,160 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '1' +display: + attachment_1: + display_options: + displays: + default: default + page_1: page_1 + pager: + type: some + display_plugin: attachment + display_title: Attachment + id: attachment_1 + position: '0' + attachment_2: + display_options: + displays: + default: default + page_1: page_1 + pager: + type: some + display_plugin: attachment + display_title: Attachment + id: attachment_2 + position: '0' + default: + display_options: + access: + type: none + arguments: + created_day: + default_argument_type: fixed + field: created_day + id: created_day + style_plugin: default_summary + table: node + created_fulldate: + default_argument_type: fixed + field: created_fulldate + id: created_fulldate + style_plugin: default_summary + table: node + created_month: + default_argument_type: fixed + field: created_month + id: created_month + style_plugin: default_summary + table: node + cache: + type: none + empty: + area: + empty: '0' + field: area + id: area + table: views + area_1: + empty: '0' + field: area + id: area_1 + table: views + exposed_form: + type: basic + fields: + created: + field: created + id: created + table: node + nid: + field: nid + id: nid + table: node + path: + field: path + id: path + table: node + filters: + nid: + field: nid + id: nid + table: node + status: + field: status + id: status + table: node + title: + field: title + id: title + table: node + footer: + area: + empty: '0' + field: area + id: area + table: views + area_1: + empty: '0' + field: area + id: area_1 + table: views + header: + area: + empty: '0' + field: area + id: area + table: views + area_1: + empty: '0' + field: area + id: area_1 + table: views + pager: + type: full + query: + type: views_query + relationships: + cid: + field: cid + id: cid + table: node + pid: + field: pid + id: pid + table: comment + relationship: cid + uid: + field: uid + id: uid + table: comment + relationship: cid + sorts: + last_comment_name: + field: last_comment_name + id: last_comment_name + table: node_comment_statistics + last_comment_timestamp: + field: last_comment_timestamp + id: last_comment_timestamp + table: node_comment_statistics + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' + page_1: + display_options: + path: test_destroy + display_plugin: page + display_title: Page + id: page_1 + position: '0' +human_name: '' +name: test_destroy +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_display.yml b/core/modules/views/tests/views_test_config/config/views.view.test_display.yml new file mode 100644 index 0000000000000000000000000000000000000000..5c44bb22d83ab19e6eb73c9f0d158757cc2193b5 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_display.yml @@ -0,0 +1,87 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '1' +display: + block: + display_options: + defaults: + pager: '0' + pager_options: '0' + row_options: '0' + row_plugin: '0' + style_options: '0' + style_plugin: '0' + field: + title: + link_to_node: '1' + pager: + options: + items_per_page: '5' + type: some + pager_options: { } + row_options: + build_mode: teaser + comments: '0' + links: '1' + row_plugin: fields + style_options: { } + style_plugin: default + display_plugin: block + display_title: Block + id: block + position: '2' + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + title: + field: title + id: title + table: node + filters: + status: + field: status + group: '1' + id: status + table: node + value: '1' + pager: + options: + items_per_page: '10' + type: full + query: + type: views_query + row_options: + build_mode: teaser + comments: '0' + links: '1' + row_plugin: node + sorts: + created: + field: created + id: created + order: DESC + table: node + style_plugin: default + title: 'Test Display' + display_plugin: default + display_title: Master + id: default + position: '0' + page: + display_options: + path: test-display + display_plugin: page + display_title: Page + id: page + position: '1' +human_name: '' +name: test_display +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_executable_displays.yml b/core/modules/views/tests/views_test_config/config/views.view.test_executable_displays.yml new file mode 100644 index 0000000000000000000000000000000000000000..8feb580c84fd1d40497865ad7dca8eced52d3975 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_executable_displays.yml @@ -0,0 +1,24 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_plugin: default + display_title: Master + id: default + position: '0' + page: + display_plugin: page + display_title: Page + id: page + position: '1' + page_2: + display_plugin: page + display_title: Page 2 + id: page_2 + position: '2' +human_name: '' +name: test_executable_displays +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_exposed_form.yml b/core/modules/views/tests/views_test_config/config/views.view.test_exposed_form.yml new file mode 100644 index 0000000000000000000000000000000000000000..736ba039e2a1e129d20eb98e3d928b3a7dab22b3 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_exposed_form.yml @@ -0,0 +1,27 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: test_exposed_form + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_exposed_form +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_feed_display.yml b/core/modules/views/tests/views_test_config/config/views.view.test_feed_display.yml new file mode 100644 index 0000000000000000000000000000000000000000..6ba9d11c9c6468bbda37d0f78cd81a989460a05a --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_feed_display.yml @@ -0,0 +1,91 @@ +api_version: '3.0' +base_table: node +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '0' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '0' + empty_zero: '0' + field: title + hide_empty: '0' + id: title + label: '' + link_to_node: '1' + table: node + filters: + status: + expose: + operator: '0' + field: status + group: '1' + id: status + table: node + value: '1' + pager: + options: + items_per_page: '10' + type: full + query: + type: views_query + row: + options: + build_mode: teaser + comments: '0' + links: '1' + type: node + sorts: + created: + field: created + id: created + order: DESC + table: node + style: + type: default + title: test_feed_display + display_plugin: default + display_title: Master + id: default + position: '0' + feed: + display_options: + displays: {} + pager: + type: some + path: test-feed-display.xml + row: + type: node_rss + style: + type: rss + display_plugin: feed + display_title: Feed + id: feed + position: '0' + page: + display_options: + path: test-feed-display + display_plugin: page + display_title: Page + id: page + position: '0' +human_name: test_feed_display +module: views +name: test_feed_display +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_field_get_entity.yml b/core/modules/views/tests/views_test_config/config/views.view.test_field_get_entity.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ba2fc94b078237dc57c53c858f17fa214ccd315 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_field_get_entity.yml @@ -0,0 +1,64 @@ +api_version: '3.0' +base_table: comment +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + cid: + field: cid + id: cid + table: comment + nid: + field: nid + id: nid + table: node + relationship: nid + uid: + field: uid + id: uid + table: users + relationship: uid + filter_groups: + groups: { } + operator: AND + filters: { } + pager: + type: full + query: + type: views_query + relationships: + nid: + field: nid + id: nid + required: '1' + table: comment + uid: + admin_label: '' + field: uid + group_type: group + id: uid + label: author + relationship: nid + required: '0' + table: node + sorts: { } + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_field_get_entity +name: test_field_get_entity +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_field_tokens.yml b/core/modules/views/tests/views_test_config/config/views.view.test_field_tokens.yml new file mode 100644 index 0000000000000000000000000000000000000000..01431a6b702f9d62f5998b529f3313733f6b575a --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_field_tokens.yml @@ -0,0 +1,45 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + type: full + query: + type: views_query + fields: + name: + id: name + table: views_test_data + field: name + name_1: + id: name_1 + table: views_test_data + field: name + name_2: + id: name_2 + table: views_test_data + field: name + job: + id: job + table: views_test_data + field: job + style: + type: default + row: + type: fields + display_plugin: default + display_title: Defaults + id: default + position: '0' +name: test_field_tokens +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_field_type.yml b/core/modules/views/tests/views_test_config/config/views.view.test_field_type.yml new file mode 100644 index 0000000000000000000000000000000000000000..c9172bc550a5ec2e67e2a34023137c572ae6e4a8 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_field_type.yml @@ -0,0 +1,20 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + fields: + type: + field: type + id: type + table: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_field_type +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_filter_date_between.yml b/core/modules/views/tests/views_test_config/config/views.view.test_filter_date_between.yml new file mode 100644 index 0000000000000000000000000000000000000000..71f4b2f256598c5ccaed069cca562752307ee149 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_filter_date_between.yml @@ -0,0 +1,41 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + nid: + field: nid + id: nid + table: node + filters: + created: + field: created + id: created + table: node + pager: + type: full + query: + options: + query_comment: '0' + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_filter_date_between +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_filter_group_override.yml b/core/modules/views/tests/views_test_config/config/views.view.test_filter_group_override.yml new file mode 100644 index 0000000000000000000000000000000000000000..379eff630c846088598cf66f2c5867c9dfda978c --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_filter_group_override.yml @@ -0,0 +1,54 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + ellipsis: '0' + word_boundary: '0' + field: title + id: title + label: '' + table: node + filters: + status: + expose: + operator: '0' + field: status + group: '1' + id: status + table: node + value: '1' + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' + page_1: + display_options: + path: test + display_plugin: page + display_title: Page + id: page_1 + position: '0' +human_name: test_filter_group_override +name: test_filter_group_override +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_filter_groups.yml b/core/modules/views/tests/views_test_config/config/views.view.test_filter_groups.yml new file mode 100644 index 0000000000000000000000000000000000000000..d71c02e1c809abc81ec08fc99379a69436f9e36b --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_filter_groups.yml @@ -0,0 +1,111 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + ellipsis: '0' + word_boundary: '0' + field: title + id: title + label: '' + table: node + filter_groups: + groups: + 1: AND + 2: AND + filters: + nid: + field: nid + group: '2' + id: nid + table: node + value: + value: '1' + nid_1: + field: nid + group: '2' + id: nid_1 + table: node + value: + value: '2' + status: + expose: + operator: '0' + field: status + group: '1' + id: status + table: node + value: '1' + pager: + options: + items_per_page: '10' + type: full + query: + type: views_query + sorts: + created: + field: created + id: created + order: DESC + table: node + title: test_filter_groups + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' + page: + display_options: + defaults: + filters: '0' + filter_groups: + groups: + 1: OR + 2: OR + operator: OR + filters: + nid: + field: nid + group: '2' + id: nid + table: node + value: + value: '1' + nid_1: + field: nid + group: '2' + id: nid_1 + table: node + value: + value: '2' + status: + expose: + operator: '0' + field: status + group: '1' + id: status + table: node + value: '1' + path: test-filter-groups + display_plugin: page + display_title: Page + id: page + position: '0' +human_name: test_filter_groups +name: test_filter_groups +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_filter_node_uid_revision.yml b/core/modules/views/tests/views_test_config/config/views.view.test_filter_node_uid_revision.yml new file mode 100644 index 0000000000000000000000000000000000000000..69000ec69d19337ed316c911ad1ee502b16d83e0 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_filter_node_uid_revision.yml @@ -0,0 +1,50 @@ +api_version: '3.0' +base_table: node +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + nid: + id: nid + table: node + field: nid + filter_groups: + groups: + 1: AND + operator: AND + filters: + uid_revision: + admin_label: '' + field: uid_revision + id: uid_revision + is_grouped: '0' + operator: in + relationship: none + table: node + value: + - '1' + sorts: { } + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_filter_node_uid_revision +name: test_filter_node_uid_revision +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_glossary.yml b/core/modules/views/tests/views_test_config/config/views.view.test_glossary.yml new file mode 100644 index 0000000000000000000000000000000000000000..33045e575a7ab3dd0c2a3dc8ed5ee8b2a1a121c3 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_glossary.yml @@ -0,0 +1,48 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + arguments: + title: + default_argument_type: fixed + field: title + glossary: '1' + id: title + limit: '1' + summary: + format: default_summary + number_of_records: '0' + summary_options: + items_per_page: '25' + table: node + cache: + type: none + exposed_form: + type: basic + fields: + title: + field: title + id: title + label: '' + table: node + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_glossary +name: test_glossary +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_group_by_count.yml b/core/modules/views/tests/views_test_config/config/views.view.test_group_by_count.yml new file mode 100644 index 0000000000000000000000000000000000000000..7ea0ab80c7d6a0658a2716d4ecd21a5b63ef6fb4 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_group_by_count.yml @@ -0,0 +1,60 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + nid: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: nid + group_type: { } + hide_empty: '0' + id: nid + link_to_node: '0' + table: node + type: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: type + hide_empty: '0' + id: type + link_to_node: '0' + table: node + group_by: '1' + pager: + type: some + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_group_by_count +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_group_by_in_filters.yml b/core/modules/views/tests/views_test_config/config/views.view.test_group_by_in_filters.yml new file mode 100644 index 0000000000000000000000000000000000000000..c494b095e9eaadcac3ef6557916c68c50f2253de --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_group_by_in_filters.yml @@ -0,0 +1,53 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + type: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: type + hide_empty: '0' + id: type + link_to_node: '0' + table: node + filters: + nid: + field: nid + group_type: count + id: nid + operator: '>' + table: node + value: + value: '3' + group_by: '1' + pager: + type: some + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_group_by_in_filters +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_term.yml b/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_term.yml new file mode 100644 index 0000000000000000000000000000000000000000..5240f0c50a08f4c53a99d996a5d1855eaf49eda6 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_term.yml @@ -0,0 +1,66 @@ +api_version: '3.0' +base_field: tid +base_table: taxonomy_term_data +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + name: + field: name + id: name + table: taxonomy_term_data + nid: + field: nid + id: nid + relationship: tid_representative + table: node + pager: + options: + items_per_page: '10' + type: full + query: + type: views_query + relationships: + tid_representative: + admin_label: '' + field: tid_representative + group_type: group + id: tid_representative + label: 'Representative node' + relationship: none + required: '0' + subquery_namespace: '' + subquery_order: DESC + subquery_regenerate: '1' + subquery_sort: node.nid + subquery_view: '' + table: taxonomy_term_data + row: + type: fields + sorts: + tid: + field: tid + id: tid + order: DESC + table: taxonomy_term_data + style: + type: default + title: test_groupwise + display_plugin: default + display_title: Master + id: default + position: { } +human_name: test_groupwise +langcode: und +module: views +name: test_groupwise_term +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_user.yml b/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_user.yml new file mode 100644 index 0000000000000000000000000000000000000000..7d79d1123d525b6715f0c3e16db4fd027f34bff4 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_groupwise_user.yml @@ -0,0 +1,76 @@ +api_version: '3.0' +base_field: uid +base_table: users +core: 8.0-dev +description: '' +disabled: '0' +display: + default: + display_options: + access: + perm: 'access user profiles' + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + name: + field: name + id: name + table: users + nid: + field: nid + id: nid + relationship: uid_representative + table: node + filters: + status: + expose: + operator: '0' + field: status + group: '1' + id: status + table: users + value: '1' + pager: + options: + items_per_page: '10' + type: full + query: + type: views_query + relationships: + uid_representative: + admin_label: '' + field: uid_representative + group_type: group + id: uid_representative + label: 'Representative node' + relationship: none + required: '0' + subquery_namespace: '' + subquery_order: DESC + subquery_regenerate: '1' + subquery_sort: node.nid + subquery_view: '' + table: users + row: + type: fields + sorts: + created: + field: created + id: created + order: DESC + table: users + style: + type: default + title: test_groupwise_user + display_plugin: default + display_title: Master + id: default + position: { } +human_name: test_groupwise_user +langcode: und +module: views +name: test_groupwise_user +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_handler_relationships.yml b/core/modules/views/tests/views_test_config/config/views.view.test_handler_relationships.yml new file mode 100644 index 0000000000000000000000000000000000000000..409fb2c0c3767ed6bdc105c32e2ca8e26d34c2bf --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_handler_relationships.yml @@ -0,0 +1,30 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + fields: + title: + id: title + table: node + field: title + relationships: + cid: + id: cid + table: node + field: cid + nid: + id: nid + table: comment + field: nid + relationship: cid + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_handler_relationships +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_nid.yml b/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_nid.yml new file mode 100644 index 0000000000000000000000000000000000000000..cefeddab9fc2d27181cf60d22f7811a93cdf518f --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_nid.yml @@ -0,0 +1,36 @@ +name: test_node_revision_nid +base_table: node_revision +core: 8 +api_version: 3 +display: + default: + display_options: + relationships: + nid: + id: nid + table: node_revision + field: nid + required: TRUE + fields: + vid: + id: vid + table: node_revision + field: vid + nid_1: + id: nid_1 + table: node_revision + field: nid + nid: + id: nid + table: node + field: nid + relationship: nid + arguments: + nid: + id: nid: + table: node_revision + field: nid + display_plugin: default + display_title: Master + id: default + position: '0' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_vid.yml b/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_vid.yml new file mode 100644 index 0000000000000000000000000000000000000000..b36f54967dcaec2ff3e73879202864decde8168c --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_node_revision_vid.yml @@ -0,0 +1,36 @@ +name: test_node_revision_vid +base_table: node_revision +core: 8 +api_version: 3 +display: + default: + display_options: + relationships: + vid: + id: vid + table: node_revision + field: vid + required: TRUE + fields: + vid: + id: vid + table: node_revision + field: vid + nid_1: + id: nid_1 + table: node_revision + field: nid + nid: + id: nid + table: node + field: nid + relationship: vid + arguments: + nid: + id: nid: + table: node_revision + field: nid + display_plugin: default + display_title: Master + id: default + position: '0' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_pager_full.yml b/core/modules/views/tests/views_test_config/config/views.view.test_pager_full.yml new file mode 100644 index 0000000000000000000000000000000000000000..08be354c67c0985333a33627e90eef8c1b7eedc9 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_pager_full.yml @@ -0,0 +1,31 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + options: + id: '0' + items_per_page: '5' + offset: '0' + type: full + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_pager_full +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_pager_none.yml b/core/modules/views/tests/views_test_config/config/views.view.test_pager_none.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b2031611986427d67d82185b73ca64a17745e1e --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_pager_none.yml @@ -0,0 +1,27 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + type: none + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_pager_none +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_pager_some.yml b/core/modules/views/tests/views_test_config/config/views.view.test_pager_some.yml new file mode 100644 index 0000000000000000000000000000000000000000..d9ecda50f46b0446f1d88709a18a2b5e6878c50d --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_pager_some.yml @@ -0,0 +1,30 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + options: + items_per_page: '5' + offset: '0' + type: some + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_pager_some +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_plugin_argument_default_current_user.yml b/core/modules/views/tests/views_test_config/config/views.view.test_plugin_argument_default_current_user.yml new file mode 100644 index 0000000000000000000000000000000000000000..54180b6612d7a2d1c6242759d0333da54e1bd910 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_plugin_argument_default_current_user.yml @@ -0,0 +1,56 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + 'null': + default_action: default + default_argument_type: current_user + field: 'null' + id: 'null' + must_not_be: '0' + style_plugin: default_summary + table: views + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: title + hide_empty: '0' + id: title + link_to_node: '0' + table: node + pager: + options: + id: '0' + items_per_page: '10' + offset: '0' + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_plugin_argument_default_current_user +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_redirect_view.yml b/core/modules/views/tests/views_test_config/config/views.view.test_redirect_view.yml new file mode 100644 index 0000000000000000000000000000000000000000..121769301b46daae85f58d440b9fe764262541d2 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_redirect_view.yml @@ -0,0 +1,79 @@ +base_table: node +name: test_redirect_view +description: '' +tag: '' +human_name: test_redirect_view +core: 8.x +api_version: '3.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: node + options: + build_mode: teaser + links: '1' + comments: '0' + fields: + 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' + 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_redirect_view + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: '' + display_options: + path: test-redirect-view +base_field: nid +disabled: '0' +module: views +langcode: und diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_store_pager_settings.yml b/core/modules/views/tests/views_test_config/config/views.view.test_store_pager_settings.yml new file mode 100644 index 0000000000000000000000000000000000000000..0c4acfe11cb168f6f067894b0eb5cbfda165084f --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_store_pager_settings.yml @@ -0,0 +1,27 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + type: none + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_store_pager_settings +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_taxonomy_node_term_data.yml b/core/modules/views/tests/views_test_config/config/views.view.test_taxonomy_node_term_data.yml new file mode 100644 index 0000000000000000000000000000000000000000..383766a9e991f8b328ff687a8cf9d2421896a618 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_taxonomy_node_term_data.yml @@ -0,0 +1,73 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + arguments: + tid: + default_argument_type: fixed + field: tid + id: tid + relationship: term_node_tid + summary: + format: default_summary + number_of_records: '0' + summary_options: + items_per_page: '25' + table: taxonomy_term_data + tid_1: + default_argument_type: fixed + field: tid + id: tid_1 + relationship: term_node_tid_1 + summary: + format: default_summary + number_of_records: '0' + summary_options: + items_per_page: '25' + table: taxonomy_term_data + cache: + type: none + exposed_form: + type: basic + pager: + type: full + query: + type: views_query + relationships: + term_node_tid: + field: term_node_tid + id: term_node_tid + label: 'Term #1' + table: node + vocabularies: + tags: '0' + term_node_tid_1: + field: term_node_tid + id: term_node_tid_1 + label: 'Term #2' + table: node + vocabularies: + tags: '0' + sorts: + nid: + field: nid + id: nid + order: DESC + table: nid + style: + type: default + row: + type: node + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_taxonomy_node_term_data +name: test_taxonomy_node_term_data +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_tokens.yml b/core/modules/views/tests/views_test_config/config/views.view.test_tokens.yml new file mode 100644 index 0000000000000000000000000000000000000000..f6e640c6bf4d34f953e76958959af4212197dd66 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_tokens.yml @@ -0,0 +1,57 @@ +base_table: views_test_data +name: test_tokens +description: 'Test view to token replacement tests.' +tag: '' +human_name: 'Test tokens' +core: 8.x +api_version: '3.0' +display: + default: + display_options: + title: 'Test token default' + defaults: + fields: '0' + pager: '0' + pager_options: '0' + sorts: '0' + fields: + age: + field: age + id: age + relationship: none + table: views_test_data + id: + field: id + id: id + relationship: none + table: views_test_data + name: + field: name + id: name + relationship: none + table: views_test_data + pager: + type: full + options: + items_per_page: 10 + pager_options: { } + display_plugin: default + display_title: Master + id: default + position: '0' + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: '1' + display_options: + defaults: + title: '0' + title: 'Test token page' + query: + type: views_query + options: { } + path: test_tokens +base_field: id +disabled: '0' +module: views diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_user_relationship.yml b/core/modules/views/tests/views_test_config/config/views.view.test_user_relationship.yml new file mode 100644 index 0000000000000000000000000000000000000000..096fa1624218a849d68e55aa88ea08523c8a5fe6 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_user_relationship.yml @@ -0,0 +1,102 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + cache: + type: none + exposed_form: + type: basic + fields: + name: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '1' + external: '0' + html: '0' + make_link: '0' + nl2br: '0' + replace_spaces: '0' + strip_tags: '0' + trim: '0' + trim_whitespace: '0' + word_boundary: '1' + element_default_classes: '1' + element_label_colon: '1' + empty_zero: '0' + field: name + hide_alter_empty: '0' + hide_empty: '0' + id: name + link_to_user: '1' + overwrite_anonymous: '0' + table: users + title: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '0' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '0' + empty_zero: '0' + field: title + hide_empty: '0' + id: title + label: '' + link_to_node: '1' + table: node + uid: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '1' + external: '0' + html: '0' + make_link: '0' + nl2br: '0' + replace_spaces: '0' + strip_tags: '0' + trim: '0' + trim_whitespace: '0' + word_boundary: '1' + element_default_classes: '1' + element_label_colon: '1' + empty_zero: '0' + field: uid + hide_alter_empty: '0' + hide_empty: '0' + id: uid + link_to_user: '1' + table: users + pager: + options: + items_per_page: '10' + type: full + query: + options: + query_comment: '0' + type: views_query + title: test_user_relationship + style: + type: default + row: + type: fields + options: + default_field_elements: '1' + hide_empty: '0' + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_user_relationship +name: test_user_relationship +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view.yml new file mode 100644 index 0000000000000000000000000000000000000000..57dd103698cb826fe0aea195a1fb6b225f3717df --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view.yml @@ -0,0 +1,48 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + defaults: + fields: '0' + pager: '0' + pager_options: '0' + sorts: '0' + fields: + age: + field: age + id: age + relationship: none + table: views_test_data + id: + field: id + id: id + relationship: none + table: views_test_data + name: + field: name + id: name + relationship: none + table: views_test_data + pager: + options: + offset: '0' + type: none + pager_options: { } + sorts: + id: + field: id + id: id + order: ASC + relationship: none + table: views_test_data + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_numeric.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_numeric.yml new file mode 100644 index 0000000000000000000000000000000000000000..446aa63bd4b043aa4f13080683c5f93b606d3bc5 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_numeric.yml @@ -0,0 +1,37 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + 'null': + default_argument_type: fixed + field: 'null' + id: 'null' + must_not_be: '0' + style_plugin: default_summary + table: views + validate: + type: numeric + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view_argument_validate_numeric +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_php.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_php.yml new file mode 100644 index 0000000000000000000000000000000000000000..1b4dc28609fe6e02fcbae97bb4e9dc2fdbdc5308 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_php.yml @@ -0,0 +1,37 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + 'null': + default_argument_type: fixed + field: 'null' + id: 'null' + must_not_be: '0' + style_plugin: default_summary + table: views + validate: + type: php + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view_argument_validate_php +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_user.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_user.yml new file mode 100644 index 0000000000000000000000000000000000000000..2750f437e4489c83ac872ce9552a8bb1b7ed91b2 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_user.yml @@ -0,0 +1,37 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + arguments: + 'null': + default_argument_type: fixed + field: 'null' + id: 'null' + must_not_be: '0' + style_plugin: default_summary + table: views + validate: + type: user + cache: + type: none + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view_argument_validate_user +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_delete.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_delete.yml new file mode 100644 index 0000000000000000000000000000000000000000..ca28db84c29d8d7aea603b03ba93e9d0c6610748 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_delete.yml @@ -0,0 +1,29 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Defaults + id: default + position: '0' +human_name: test_view_delete +name: test_view_delete +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_fieldapi.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_fieldapi.yml new file mode 100644 index 0000000000000000000000000000000000000000..fe2ecc0b4458f8c3193bc4ab0d50d9d3ddd13968 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_fieldapi.yml @@ -0,0 +1,34 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: perm + fields: + nid: + field: nid + id: nid + table: node + cache: + type: none + exposed_form: + type: basic + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_view_fieldapi +name: test_view_fieldapi +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_handler_weight.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_handler_weight.yml new file mode 100644 index 0000000000000000000000000000000000000000..cf47d54e271768dce8b12a1d9f99b4fc914bafe0 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_handler_weight.yml @@ -0,0 +1,63 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + defaults: + fields: '0' + pager: '0' + pager_options: '0' + sorts: '0' + fields: + id: + field: id + id: id + relationship: none + table: views_test_data + age: + field: age + id: age + relationship: none + table: views_test_data + name: + field: name + id: name + relationship: none + table: views_test_data + pager: + options: + offset: '0' + type: none + pager_options: { } + sorts: + id: + field: id + id: id + order: ASC + relationship: none + table: views_test_data + age: + field: age + id: standard + order: ASC + relationship: none + table: views_test_data + filters: + name: + field: name + id: string + table: views_test_data + age: + field: age + id: numeric + table: views_test_data + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view_handler_weight +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_pager_full_zero_items_per_page.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_pager_full_zero_items_per_page.yml new file mode 100644 index 0000000000000000000000000000000000000000..5b909ea4c626656e1b2c7e49c7887d62262649bc --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_pager_full_zero_items_per_page.yml @@ -0,0 +1,47 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + title: + alter: + alter_text: '0' + ellipsis: '1' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '1' + empty_zero: '0' + field: title + hide_empty: '0' + id: title + link_to_node: '0' + table: node + pager: + options: + id: '0' + items_per_page: '0' + offset: '0' + type: full + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_view_pager_full_zero_items_per_page +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_view_status.yml b/core/modules/views/tests/views_test_config/config/views.view.test_view_status.yml new file mode 100644 index 0000000000000000000000000000000000000000..08261f55df31e8c45b3e056300d1e054391347a2 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_view_status.yml @@ -0,0 +1,16 @@ +base_table: views_test_data +name: test_view_status +description: '' +tag: '' +human_name: test_view_status +core: 8.x +api_version: '3.0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' +base_field: id +disabled: '1' +module: views diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_views_handler_field_user_name.yml b/core/modules/views/tests/views_test_config/config/views.view.test_views_handler_field_user_name.yml new file mode 100644 index 0000000000000000000000000000000000000000..48522e05f15dac1115fad815dca48d27d696b285 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_views_handler_field_user_name.yml @@ -0,0 +1,50 @@ +api_version: '3.0' +base_table: users +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + name: + alter: + absolute: '0' + alter_text: '0' + ellipsis: '0' + html: '0' + make_link: '0' + strip_tags: '0' + trim: '0' + word_boundary: '0' + empty_zero: '0' + field: name + hide_empty: '0' + id: name + label: '' + link_to_user: '1' + overwrite_anonymous: '0' + table: users + pager: + type: full + query: + options: + query_comment: '0' + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: test_views_handler_field_user_name +name: test_views_handler_field_user_name +tag: default diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_field.yml b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_field.yml new file mode 100644 index 0000000000000000000000000000000000000000..22642b54b85fcf76e793f43e38b496b0979a2cd6 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_field.yml @@ -0,0 +1,20 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + fields: + old_field_1: + field: old_field_1 + id: old_field_1 + table: views_test_data + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_views_move_to_field +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_handler.yml b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_handler.yml new file mode 100644 index 0000000000000000000000000000000000000000..d66c95dd840d56788545dc24cb9f5cdddf93f099 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_handler.yml @@ -0,0 +1,25 @@ +api_version: '3.0' +base_table: views_test_data +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + fields: + old_field_2: + field: old_field_2 + id: old_field_2 + table: views_test_data + filters: + old_field_3: + field: old_field_3 + id: old_field_3 + table: views_test_data + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_views_move_to_handler +tag: '' diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_table.yml b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_table.yml new file mode 100644 index 0000000000000000000000000000000000000000..956a7f0e24de93517716aae054ac43446e5bf6c8 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_table.yml @@ -0,0 +1,20 @@ +api_version: '3.0' +base_table: views_old_table +core: '8' +description: '' +disabled: '0' +display: + default: + display_options: + fields: + id: + field: id + id: id + table: views_old_table + display_plugin: default + display_title: Master + id: default + position: '0' +human_name: '' +name: test_views_move_to_table +tag: '' diff --git a/core/modules/views/tests/views_test_config/views_test_config.info b/core/modules/views/tests/views_test_config/views_test_config.info new file mode 100644 index 0000000000000000000000000000000000000000..625076c71da06c57faa29c71cde2e3a12a7662ef --- /dev/null +++ b/core/modules/views/tests/views_test_config/views_test_config.info @@ -0,0 +1,7 @@ +name = Views Test Config +description = Provides default views for tests. +package = Testing +version = VERSION +core = 8.x +dependencies[] = views +hidden = TRUE diff --git a/core/modules/views/tests/views_test_config/views_test_config.module b/core/modules/views/tests/views_test_config/views_test_config.module new file mode 100644 index 0000000000000000000000000000000000000000..b3d9bbc7f3711e882119cd6b3af051245d859d04 --- /dev/null +++ b/core/modules/views/tests/views_test_config/views_test_config.module @@ -0,0 +1 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + public function access($account) { + return !empty($this->options['access']) && isset($this->view->args[0]) && $this->view->args[0] == variable_get('test_dynamic_access_argument1', NULL) && isset($this->view->args[1]) && $this->view->args[1] == variable_get('test_dynamic_access_argument2', NULL); + } + + function get_access_callback() { + return array('views_test_data_test_dynamic_access_callback', array(!empty($options['access']), 1, 2)); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5f5770c03d0ea2bdb9773efeba0d61257662bf8b --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php @@ -0,0 +1,40 @@ + FALSE, 'bool' => TRUE); + + return $options; + } + + public function access($account) { + return !empty($this->options['access']); + } + + function get_access_callback() { + return array('views_test_data_test_static_access_callback', array(!empty($options['access']))); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/area/TestExample.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/area/TestExample.php new file mode 100644 index 0000000000000000000000000000000000000000..78efe6490cb8d800c4710055d71a7bd01ed57ebd --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/area/TestExample.php @@ -0,0 +1,43 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\area\AreaPluginBase::render(). + */ + public function render($empty = FALSE) { + if (!$empty || !empty($this->options['empty'])) { + return $this->options['string']; + } + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/argument_default/ArgumentDefaultTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/argument_default/ArgumentDefaultTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c29fc8dbebfee91963958a3546dba5c22436a85b --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/argument_default/ArgumentDefaultTest.php @@ -0,0 +1,41 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase::get_argument(). + */ + public function get_argument() { + return $this->options['value']; + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1cc7ef5c7a4f2a58829fb3d62858420a1ef06c6c --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php @@ -0,0 +1,134 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::optionsSummaryv(). + */ + public function optionsSummary(&$categories, &$options) { + parent::optionsSummary($categories, $options); + + $categories['display_test'] = array( + 'title' => t('Display test settings'), + 'column' => 'second', + 'build' => array( + '#weight' => -100, + ), + ); + + $test_option = $this->getOption('test_option') ?: t('Empty'); + + $options['test_option'] = array( + 'category' => 'display_test', + 'title' => t('Test option'), + 'value' => views_ui_truncate($test_option, 24), + ); + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + switch ($form_state['section']) { + case 'test_option': + $form['#title'] .= t('Test option'); + $form['test_option'] = array( + '#type' => 'textfield', + '#description' => t('This is a textfield for test_option.'), + '#default_value' => $this->getOption('test_option'), + ); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::validateOptionsForm(). + */ + public function validateOptionsForm(&$form, &$form_state) { + parent::validateOptionsForm($form, $form_state); + watchdog('views', $form_state['values']['test_option']); + switch ($form_state['section']) { + case 'test_option': + if (!trim($form_state['values']['test_option'])) { + form_error($form['test_option'], t('You cannot have an empty option.')); + } + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::submitOptionsForm(). + */ + public function submitOptionsForm(&$form, &$form_state) { + parent::submitOptionsForm($form, $form_state); + switch ($form_state['section']) { + case 'test_option': + $this->setOption('test_option', $form_state['values']['test_option']); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::execute(). + */ + public function execute() { + $this->view->build(); + + // Render the test option as the title before the view output. + $render = ' ' . filter_xss_admin($this->options['test_option']) . '
'; + // And now render the view. + $render .= $this->view->render(); + + return $render; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::preview(). + * + * Override so preview and execute are the same output. + */ + public function preview() { + return $this->execute(); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..63c9f6db9d24ea6702461f3059e9a1f94a432b61 --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest.php @@ -0,0 +1,111 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::optionsSummary(). + */ + public function optionsSummary(&$categories, &$options) { + parent::optionsSummary($categories, $options); + + $categories['display_extender_test'] = array( + 'title' => t('Display extender test settings'), + 'column' => 'second', + 'build' => array( + '#weight' => -100, + ), + ); + + $test_option = $this->displayHandler->getOption('test_extender_test_option') ?: t('Empty'); + + $options['test_extender_test_option'] = array( + 'category' => 'display_extender_test', + 'title' => t('Test option'), + 'value' => views_ui_truncate($test_option, 24), + ); + } + + /** + * Overrides Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + switch ($form_state['section']) { + case 'test_extender_test_option': + $form['#title'] .= t('Test option'); + $form['test_extender_test_option'] = array( + '#type' => 'textfield', + '#description' => t('This is a textfield for test_option.'), + '#default_value' => $this->displayHandler->getOption('test_extender_test_option'), + ); + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayExtenderPluginBase::submitOptionsForm(). + */ + public function submitOptionsForm(&$form, &$form_state) { + parent::submitOptionsForm($form, $form_state); + switch ($form_state['section']) { + case 'test_extender_test_option': + $this->displayHandler->setOption('test_extender_test_option', $form_state['values']['test_extender_test_option']); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayExtenderPluginBase::defaultableSections(). + */ + public function defaultableSections(&$sections, $section = NULL) { + $sections['test_extender_test_option'] = array('test_extender_test_option'); + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayExtenderPluginBase::query(). + */ + public function query() { + $this->testState['query'] = TRUE; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayExtenderPluginBase::pre_execute(). + */ + public function pre_execute() { + $this->testState['pre_execute'] = TRUE; + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest2.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest2.php new file mode 100644 index 0000000000000000000000000000000000000000..aa83221fec2bb4c9835bf9608d09b508da972627 --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest2.php @@ -0,0 +1,23 @@ +testValue = $value; + } + + /** + * Returns the testValue property. + * + * @return string + */ + public function getTestValue() { + return $this->testValue; + } + + /** + * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::add_self_tokens(). + */ + function add_self_tokens(&$tokens, $item) { + $tokens['[test-token]'] = $this->getTestValue(); + } + + /** + * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::render(). + */ + function render($values) { + return $this->sanitizeValue($this->getTestValue()); + } + + /** + * A mock function which allows to call placeholder from public. + * + * @return string + * The result of the placeholder method. + */ + public function getPlaceholder() { + return $this->placeholder(); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/filter/FilterTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/filter/FilterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fa92dc298c582d5115368ec56ca986bb8de6d09e --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/filter/FilterTest.php @@ -0,0 +1,62 @@ + TRUE, 'bool' => TRUE); + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::buildOptionsForm(). + * + * @return array + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['test_enable'] = array( + '#type' => 'checkbox', + '#title' => t('Controls whether the filter plugin should be active.'), + '#default_value' => $this->options['test_enable'], + ); + } + + /** + * Overrides Drupal\views\Plugin\views\filter\FilterPluginBase::query(). + */ + public function query() { + // Call the parent if this option is enabled. + if ($this->options['test_enable']) { + parent::query(); + } + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/join/JoinTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/join/JoinTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5d0cb73d127596802bb8dfc7fa37428211db111d --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/join/JoinTest.php @@ -0,0 +1,58 @@ +joinValue; + } + + /** + * Sets the joinValue property. + * + * @param int $join_value + */ + public function setJoinValue($join_value) { + $this->joinValue = $join_value; + } + + + /** + * Overrides Drupal\views\Plugin\views\join\JoinPluginBase::buildJoin(). + */ + public function buildJoin($select_query, $table, $view_query) { + // Add an additional hardcoded condition to the query. + $this->extra = 'node.uid = ' . $this->getJoinValue(); + parent::buildJoin($select_query, $table, $view_query); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/query/QueryTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/query/QueryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eab54df0fca2734c8354b5b1b0013be8f269bf19 --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/query/QueryTest.php @@ -0,0 +1,127 @@ +allItems = $allItems; + } + + public function add_where($group, $field, $value = NULL, $operator = NULL) { + $this->conditions[] = array( + 'field' => $field, + 'value' => $value, + 'operator' => $operator + ); + + } + + public function add_field($table, $field, $alias = '', $params = array()) { + $this->fields[$field] = $field; + return $field; + } + + public function add_orderby($table, $field = NULL, $order = 'ASC', $alias = '', $params = array()) { + $this->orderBy = array('field' => $field, 'order' => $order); + } + + + public function ensure_table($table, $relationship = NULL, JoinPluginBase $join = NULL) { + // There is no concept of joins. + } + + /** + * Implements Drupal\views\Plugin\views\query\QueryPluginBase::build(). + * + * @param Drupal\views\ViewExecutable $view + */ + public function build(ViewExecutable $view) { + $this->view = $view; + // @todo Support pagers for know, a php based one would probably match. + // @todo You could add a string representatin of the query. + $this->view->build_info['query'] = ""; + $this->view->build_info['count_query'] = ""; +} + + /** + * Implements Drupal\views\Plugin\views\query\QueryPluginBase::execute(). + */ + public function execute(ViewExecutable $view) { + $result = array(); + foreach ($this->allItems as $element) { + // Run all conditions on the element, and add it to the result if they + // match. + $match = TRUE; + foreach ($this->conditions as $condition) { + $match &= $this->match($element, $condition); + } + if ($match) { + // If the query explicit defines fields to use, filter all others out. + // Filter out fields + if ($this->fields) { + $element = array_intersect_key($element, $this->fields); + } + $result[] = (object) $element; + } + } + $this->view->result = $result; + } + + /** + * Check a single condition for a single element. + * + * @param array $element + * The element which should be checked. + * @param array $condition + * An associative array containing: + * - field: The field to by, for example id. + * - value: The expected value of the element. + * - operator: The operator to compare the element value with the expected + * value. + * + * @return bool + * Returns whether the condition matches with the element. + */ + public function match($element, $condition) { + $value = $element[$condition['field']]; + switch ($condition['operator']) { + case '=': + return $value == $condition['value']; + case 'IN': + return in_array($value, $condition['value']); + } + return FALSE; + } + + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/row/RowTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/row/RowTest.php new file mode 100644 index 0000000000000000000000000000000000000000..771ac8f32074fda587c82fb8ef82f7e632df372b --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/row/RowTest.php @@ -0,0 +1,85 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['test_option'] = array( + '#type' => 'textfield', + '#description' => t('This is a textfield for test_option.'), + '#default_value' => $this->options['test_option'], + ); + } + + /** + * Sets the output property. + * + * @param string $output + * The string to output by this plugin. + */ + public function setOutput($output) { + $this->output = $output; + } + + /** + * Returns the output property. + * + * @return string + */ + public function getOutput() { + return $this->output; + } + + /** + * Overrides Drupal\views\Plugin\views\row\RowPluginBase::render() + */ + public function render($row) { + return $this->getOutput(); + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2336d04bb4ada56189f3920accc407feabdb6906 --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php @@ -0,0 +1,70 @@ + array( + '#title' => t('Title field'), + '#description' => t('Choose the field with the custom title.'), + '#toggle' => TRUE, + '#required' => TRUE, + ), + 'name_field' => array( + '#title' => t('Name field'), + '#description' => t('Choose the field with the custom name.'), + ), + 'numeric_field' => array( + '#title' => t('Numeric field'), + '#description' => t('Select one or more numeric fields.'), + '#multiple' => TRUE, + '#toggle' => TRUE, + '#filter' => 'filterNumericFields', + '#required' => TRUE, + ), + ); + } + + /** + * Restricts the allowed fields to only numeric fields. + * + * @param array $fields + * An array of field labels, keyed by the field ID. + */ + protected function filterNumericFields(&$fields) { + foreach ($this->view->field as $id => $field) { + if (!($field instanceof Numeric)) { + unset($fields[$id]); + } + } + } + +} diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/StyleTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/StyleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..59dc396ad240da3e208d6cbe2014da88a494127b --- /dev/null +++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/StyleTest.php @@ -0,0 +1,110 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\style\StylePluginBase::buildOptionsForm(). + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['test_option'] = array( + '#type' => 'textfield', + '#description' => t('This is a textfield for test_option.'), + '#default_value' => $this->options['test_option'], + ); + } + + function usesRowPlugin() { + return parent::usesRowPlugin(); + } + + /** + * Sets the usesRowPlugin property. + * + * @param bool $status + * TRUE if this style plugin should use rows. + */ + public function setUsesRowPlugin($status) { + $this->usesRowPlugin = $status; + } + + /** + * Sets the output property. + * + * @param string $output + * The string to output by this plugin. + */ + public function setOutput($output) { + $this->output = $output; + } + + /** + * Returns the output property. + * + * @return string + */ + public function getOutput() { + return $this->output; + } + + /** + * Overrides Drupal\views\Plugin\views\style\StylePluginBase::render() + */ + public function render() { + $output = ''; + if (!$this->usesRowPlugin()) { + $output = $this->getOutput(); + } + else { + foreach ($this->view->result as $index => $row) { + $this->view->row_index = $index; + $output .= $this->row_plugin->render($row) . "\n"; + } + } + + return $output; + } + +} diff --git a/core/modules/views/tests/views_test_data/templates/views-view--frontpage.tpl.php b/core/modules/views/tests/views_test_data/templates/views-view--frontpage.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..5e6c8fabfa14a4db4d7230600b575a2325a2d669 --- /dev/null +++ b/core/modules/views/tests/views_test_data/templates/views-view--frontpage.tpl.php @@ -0,0 +1,85 @@ + +> + +diff --git a/core/modules/views/tests/views_test_data/views_cache.test.css b/core/modules/views/tests/views_test_data/views_cache.test.css new file mode 100644 index 0000000000000000000000000000000000000000..8dd17c148f598c12bcc70ef63d1af27a71e6b5bb --- /dev/null +++ b/core/modules/views/tests/views_test_data/views_cache.test.css @@ -0,0 +1,5 @@ +/** + * @file + * Just a placeholder file for the test. + * @see ViewsCacheTest::testHeaderStorage + */ diff --git a/core/modules/views/tests/views_test_data/views_cache.test.js b/core/modules/views/tests/views_test_data/views_cache.test.js new file mode 100644 index 0000000000000000000000000000000000000000..8dd17c148f598c12bcc70ef63d1af27a71e6b5bb --- /dev/null +++ b/core/modules/views/tests/views_test_data/views_cache.test.js @@ -0,0 +1,5 @@ +/** + * @file + * Just a placeholder file for the test. + * @see ViewsCacheTest::testHeaderStorage + */ diff --git a/core/modules/views/tests/views_test_data/views_test_data.info b/core/modules/views/tests/views_test_data/views_test_data.info new file mode 100644 index 0000000000000000000000000000000000000000..7d2ae101c743cc6cbe8f534611ff02ebc82e319d --- /dev/null +++ b/core/modules/views/tests/views_test_data/views_test_data.info @@ -0,0 +1,7 @@ +name = Views Test +description = Test module for Views. +package = Testing +version = VERSION +core = 8.x +dependencies[] = views +hidden = TRUE diff --git a/core/modules/views/tests/views_test_data/views_test_data.install b/core/modules/views/tests/views_test_data/views_test_data.install new file mode 100644 index 0000000000000000000000000000000000000000..bb1353131ad447e390f692c5dce34246177024e6 --- /dev/null +++ b/core/modules/views/tests/views_test_data/views_test_data.install @@ -0,0 +1,35 @@ + 'DIV', + 'span' => 'SPAN', + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'p' => 'P', + 'strong' => 'STRONG', + 'em' => 'EM', + 'marquee' => 'MARQUEE' + ); + config('views.settings')->set('field_rewrite_elements', $values)->save(); +} diff --git a/core/modules/views/tests/views_test_data/views_test_data.module b/core/modules/views/tests/views_test_data/views_test_data.module new file mode 100644 index 0000000000000000000000000000000000000000..c4a28f954167d46d6e56938563339ea5ef01ad3a --- /dev/null +++ b/core/modules/views/tests/views_test_data/views_test_data.module @@ -0,0 +1,194 @@ + array( + 'title' => t('Test permission'), + 'description' => t('views_test_data test permission'), + ), + ); +} + +/** + * Implements hook_views_api(). + */ +function views_test_data_views_api() { + return array( + 'api' => 3.0, + 'template path' => drupal_get_path('module', 'views_test_data') . '/templates', + ); +} + +/** + * Implements hook_views_data(). + */ +function views_test_data_views_data() { + return variable_get('views_test_data_views_data', array()); +} + +function views_test_data_test_static_access_callback($access) { + return $access; +} + +function views_test_data_test_dynamic_access_callback($access, $argument1, $argument2) { + return $access && $argument1 == variable_get('test_dynamic_access_argument1', NULL) && $argument2 == variable_get('test_dynamic_access_argument2', NULL); +} + +/**+ + * Access callback for the generic handler test. + * + * @return bool + * Returns views_test_data.tests->handler_access_callback config. so the user + * has access to the handler. + * + * @see Drupal\views\Tests\Handler\HandlerTest + */ +function views_test_data_handler_test_access_callback() { + return config('views_test_data.tests')->get('handler_access_callback'); +} + +/** + * Access callback with an argument for the generic handler test. + * + * @param bool $argument + * A parameter to test that an argument got passed. + * + * @return bool + * Returns views_test_data.tests->handler_access_callback_argument, so the + * use has access to the handler. + * + * @see Drupal\views\Tests\Handler\HandlerTest + */ +function views_test_data_handler_test_access_callback_argument($argument = FALSE) { + // Check the argument to be sure that access arguments are passed into the + // callback. + if ($argument) { + return config('views_test_data.tests')->get('handler_access_callback_argument'); + } + else { + return FALSE; + } +} + + +/** + * Implements hook_views_pre_render(). + */ +function views_test_data_views_pre_render(ViewExecutable $view) { + if ($view->storage->name == 'test_cache_header_storage') { + drupal_add_js(drupal_get_path('module', 'views_test_data') . '/views_cache.test.js'); + drupal_add_css(drupal_get_path('module', 'views_test_data') . '/views_cache.test.css'); + $view->build_info['pre_render_called'] = TRUE; + } +} + +/** + * Implements hook_views_post_build(). + */ +function views_test_data_views_post_build(ViewExecutable $view) { + if ($view->storage->name == 'test_page_display') { + if ($view->current_display == 'page_1') { + $view->build_info['denied'] = TRUE; + } + elseif ($view->current_display == 'page_2') { + $view->build_info['fail'] = TRUE; + } + } +} + +/** + * Implements hook_preprocess_HOOK() for theme_views_view_mapping_test(). + */ +function template_preprocess_views_view_mapping_test(&$variables) { + $variables['element'] = array(); + + foreach ($variables['rows'] as $delta => $row) { + $fields = array(); + foreach ($variables['options']['mapping'] as $type => $field_names) { + if (!is_array($field_names)) { + $field_names = array($field_names); + } + foreach ($field_names as $field_name) { + if ($value = $variables['view']->style_plugin->get_field($delta, $field_name)) { + $fields[$type . '-' . $field_name] = $type . ':' . $value; + } + } + } + + // If there are no fields in this row, skip to the next one. + if (empty($fields)) { + continue; + } + + // Build a container for the row. + $variables['element'][$delta] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'views-row-mapping-test', + ), + ), + ); + + // Add each field to the row. + foreach ($fields as $key => $render) { + $variables['element'][$delta][$key] = array( + '#children' => $render, + '#type' => 'container', + '#attributes' => array( + 'class' => array( + $key, + ), + ), + ); + } + } +} + +/** + * Returns HTML for the Mapping Test style. + */ +function theme_views_view_mapping_test($variables) { + return drupal_render($variables['element']); +} + +/** + * Implements hook_menu(). + */ +function views_test_data_menu() { + $items = array(); + + $items['views_test_data_element_form'] = array( + 'title' => 'Views test data element form', + 'description' => 'Views test data element form callback', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('views_test_data_element_form'), + 'access callback' => TRUE, + ); + + return $items; +} + +/** + * Simple form page callback to test the view element. + */ +function views_test_data_element_form() { + $form['view'] = array( + '#type' => 'view', + '#name' => 'test_view', + '#display_id' => 'default', + '#arguments' => array(25), + ); + + return $form; +} diff --git a/core/modules/views/theme/theme.inc b/core/modules/views/theme/theme.inc new file mode 100644 index 0000000000000000000000000000000000000000..40ce8fe9cf08a3d51a7836185c4e72d6f97afd35 --- /dev/null +++ b/core/modules/views/theme/theme.inc @@ -0,0 +1,1159 @@ +storage->name . '__' . $display['id']; + $themes[] = $hook . '__' . $display['id']; + // Add theme suggestions for each single tag. + foreach (drupal_explode_tags($view->storage->tag) as $tag) { + $themes[] = $hook . '__' . preg_replace('/[^a-z0-9]/', '_', strtolower($tag)); + } + + if ($display['id'] != $display['display_plugin']) { + $themes[] = $hook . '__' . $view->storage->name . '__' . $display['display_plugin']; + $themes[] = $hook . '__' . $display['display_plugin']; + } + } + $themes[] = $hook . '__' . $view->storage->name; + $themes[] = $hook; + return $themes; +} + +/** + * Preprocess the primary theme implementation for a view. + */ +function template_preprocess_views_view(&$vars) { + global $base_path; + + $view = $vars['view']; + + $vars['rows'] = (!empty($view->result) || $view->style_plugin->even_empty()) ? $view->style_plugin->render($view->result) : ''; + + $vars['css_name'] = drupal_clean_css_identifier($view->storage->name); + $vars['name'] = $view->storage->name; + $vars['display_id'] = $view->current_display; + + // Basic classes + $vars['css_class'] = ''; + + $vars['attributes']['class'] = array(); + $vars['attributes']['class'][] = 'view'; + $vars['attributes']['class'][] = 'view-' . drupal_clean_css_identifier($vars['name']); + $vars['attributes']['class'][] = 'view-id-' . $vars['name']; + $vars['attributes']['class'][] = 'view-display-id-' . $vars['display_id']; + + $css_class = $view->display_handler->getOption('css_class'); + if (!empty($css_class)) { + $vars['css_class'] = preg_replace('/[^a-zA-Z0-9- ]/', '-', $css_class); + $vars['attributes']['class'][] = $vars['css_class']; + } + + $empty = empty($vars['rows']); + + $vars['header'] = $view->display_handler->renderArea('header', $empty); + $vars['footer'] = $view->display_handler->renderArea('footer', $empty); + if ($empty) { + $vars['empty'] = $view->display_handler->renderArea('empty', $empty); + } + + $vars['exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : ''; + $vars['more'] = $view->display_handler->renderMoreLink(); + $vars['feed_icon'] = !empty($view->feed_icon) ? $view->feed_icon : ''; + + $vars['pager'] = ''; + + // @todo: Figure out whether this belongs into views_ui_preprocess_views_view. + // Render title for the admin preview. + $vars['title'] = !empty($view->views_ui_context) ? filter_xss_admin($view->getTitle()) : ''; + + if ($view->display_handler->renderPager()) { + $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL; + $vars['pager'] = $view->renderPager($exposed_input); + } + + $vars['attachment_before'] = !empty($view->attachment_before) ? $view->attachment_before : ''; + $vars['attachment_after'] = !empty($view->attachment_after) ? $view->attachment_after : ''; + + // Add contextual links to the view. We need to attach them to the dummy + // $view_array variable, since contextual_preprocess() requires that they be + // attached to an array (not an object) in order to process them. For our + // purposes, it doesn't matter what we attach them to, since once they are + // processed by contextual_preprocess() they will appear in the $title_suffix + // variable (which we will then render in views-view.tpl.php). + views_add_contextual_links($vars['view_array'], 'view', $view, $view->current_display); + + // Attachments are always updated with the outer view, never by themselves, + // so they do not have dom ids. + if (empty($view->is_attachment)) { + // Our JavaScript needs to have some means to find the HTML belonging to this + // view. + // + // It is true that the DIV wrapper has classes denoting the name of the view + // and its display ID, but this is not enough to unequivocally match a view + // with its HTML, because one view may appear several times on the page. So + // we set up a hash with the current time, $dom_id, to issue a "unique" identifier for + // each view. This identifier is written to both Drupal.settings and the DIV + // wrapper. + $vars['dom_id'] = $view->dom_id; + $vars['attributes']['class'][] = 'view-dom-id-' . $vars['dom_id']; + } + + // If using AJAX, send identifying data about this view. + if ($view->use_ajax && empty($view->is_attachment) && empty($view->live_preview)) { + $settings = array( + 'views' => array( + 'ajax_path' => url('views/ajax'), + 'ajaxViews' => array( + 'views_dom_id:' . $vars['dom_id'] => array( + 'view_name' => $view->storage->name, + 'view_display_id' => $view->current_display, + 'view_args' => check_plain(implode('/', $view->args)), + 'view_path' => check_plain(current_path()), + 'view_base_path' => $view->getPath(), + 'view_dom_id' => $vars['dom_id'], + // To fit multiple views on a page, the programmer may have + // overridden the display's pager_element. + 'pager_element' => isset($view->pager) ? $view->pager->get_pager_id() : 0, + ), + ), + ), + ); + + drupal_add_js($settings, 'setting'); + views_add_js('ajax_view'); + } + + // If form fields were found in the View, reformat the View output as a form. + if (views_view_has_form_elements($view)) { + $output = !empty($vars['rows']) ? $vars['rows'] : $vars['empty']; + $form = drupal_get_form(views_form_id($view), $view, $output); + // The form is requesting that all non-essential views elements be hidden, + // usually because the rendered step is not a view result. + if ($form['show_view_elements']['#value'] == FALSE) { + $vars['header'] = ''; + $vars['exposed'] = ''; + $vars['pager'] = ''; + $vars['footer'] = ''; + $vars['more'] = ''; + $vars['feed_icon'] = ''; + } + $vars['rows'] = $form; + } +} + +/** + * Process function to render certain elements into the view. + */ +function template_process_views_view(&$vars) { + if (is_array($vars['rows'])) { + $vars['rows'] = drupal_render($vars['rows']); + } +} + +/** + * Preprocess theme function to print a single record from a row, with fields + */ +function template_preprocess_views_view_fields(&$vars) { + $view = $vars['view']; + + // Loop through the fields for this view. + $previous_inline = FALSE; + $vars['fields'] = array(); // ensure it's at least an empty array. + foreach ($view->field as $id => $field) { + // render this even if set to exclude so it can be used elsewhere. + $field_output = $view->style_plugin->get_field($view->row_index, $id); + $empty = $field->is_value_empty($field_output, $field->options['empty_zero']); + if (empty($field->options['exclude']) && (!$empty || (empty($field->options['hide_empty']) && empty($vars['options']['hide_empty'])))) { + $object = new stdClass(); + $object->handler = &$view->field[$id]; + $object->inline = !empty($vars['options']['inline'][$id]); + + $object->element_type = $object->handler->element_type(TRUE, !$vars['options']['default_field_elements'], $object->inline); + if ($object->element_type) { + $class = ''; + if ($object->handler->options['element_default_classes']) { + $class = 'field-content'; + } + + if ($classes = $object->handler->element_classes($view->row_index)) { + if ($class) { + $class .= ' '; + } + $class .= $classes; + } + + $pre = '<' . $object->element_type; + if ($class) { + $pre .= ' class="' . $class . '"'; + } + $field_output = $pre . '>' . $field_output . '' . $object->element_type . '>'; + } + + // Protect ourself somewhat for backward compatibility. This will prevent + // old templates from producing invalid HTML when no element type is selected. + if (empty($object->element_type)) { + $object->element_type = 'span'; + } + + $object->content = $field_output; + if (isset($view->field[$id]->field_alias) && isset($vars['row']->{$view->field[$id]->field_alias})) { + $object->raw = $vars['row']->{$view->field[$id]->field_alias}; + } + else { + $object->raw = NULL; // make sure it exists to reduce NOTICE + } + + if (!empty($vars['options']['separator']) && $previous_inline && $object->inline && $object->content) { + $object->separator = filter_xss_admin($vars['options']['separator']); + } + + $object->class = drupal_clean_css_identifier($id); + + $previous_inline = $object->inline; + $object->inline_html = $object->handler->element_wrapper_type(TRUE, TRUE); + if ($object->inline_html === '' && $vars['options']['default_field_elements']) { + $object->inline_html = $object->inline ? 'span' : 'div'; + } + + // Set up the wrapper HTML. + $object->wrapper_prefix = ''; + $object->wrapper_suffix = ''; + + if ($object->inline_html) { + $class = ''; + if ($object->handler->options['element_default_classes']) { + $class = "views-field views-field-" . $object->class; + } + + if ($classes = $object->handler->element_wrapper_classes($view->row_index)) { + if ($class) { + $class .= ' '; + } + $class .= $classes; + } + + $object->wrapper_prefix = '<' . $object->inline_html; + if ($class) { + $object->wrapper_prefix .= ' class="' . $class . '"'; + } + $object->wrapper_prefix .= '>'; + $object->wrapper_suffix = '' . $object->inline_html . '>'; + } + + // Set up the label for the value and the HTML to make it easier + // on the template. + $object->label = check_plain($view->field[$id]->label()); + $object->label_html = ''; + if ($object->label) { + $object->label_html .= $object->label; + if ($object->handler->options['element_label_colon']) { + $object->label_html .= ': '; + } + + $object->element_label_type = $object->handler->element_label_type(TRUE, !$vars['options']['default_field_elements']); + if ($object->element_label_type) { + $class = ''; + if ($object->handler->options['element_default_classes']) { + $class = 'views-label views-label-' . $object->class; + } + + $element_label_class = $object->handler->element_label_classes($view->row_index); + if ($element_label_class) { + if ($class) { + $class .= ' '; + } + + $class .= $element_label_class; + } + + $pre = '<' . $object->element_label_type; + if ($class) { + $pre .= ' class="' . $class . '"'; + } + $pre .= '>'; + + $object->label_html = $pre . $object->label_html . '' . $object->element_label_type . '>'; + } + } + + $vars['fields'][$id] = $object; + } + } + +} + +/** + * Display a single views grouping. + */ +function theme_views_view_grouping($vars) { + $view = $vars['view']; + $title = $vars['title']; + $content = $vars['content']; + + $output = '+ ++ + + ++ ++ + + + + + + ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + +'; + $output .= ''; + + return $output; +} + +/** + * Process a single grouping within a view. + */ +function template_preprocess_views_view_grouping(&$vars) { + $vars['content'] = $vars['view']->style_plugin->render_grouping_sets($vars['rows'], $vars['grouping_level']); +} + +/** + * Display a single views field. + * + * Interesting bits of info: + * $field->field_alias says what the raw value in $row will be. Reach it like + * this: @code { $row->{$field->field_alias} @endcode + */ +function theme_views_view_field($vars) { + $view = $vars['view']; + $field = $vars['field']; + $row = $vars['row']; + return $vars['output']; +} + +/** + * Process a single field within a view. + * + * This preprocess function isn't normally run, as a function is used by + * default, for performance. However, by creating a template, this + * preprocess should get picked up. + */ +function template_preprocess_views_view_field(&$vars) { + $vars['output'] = $vars['field']->advanced_render($vars['row']); +} + +/** + * Preprocess theme function to print a single record from a row, with fields + */ +function template_preprocess_views_view_summary(&$vars) { + $view = $vars['view']; + $argument = $view->argument[$view->build_info['summary_level']]; + $vars['row_classes'] = array(); + + $url_options = array(); + + if (!empty($view->exposed_raw_input)) { + $url_options['query'] = $view->exposed_raw_input; + } + + $active_urls = drupal_map_assoc(array( + url(current_path(), array('alias' => TRUE)), // force system path + url(current_path()), // could be an alias + )); + + // Collect all arguments foreach row, to be able to alter them for example by the validator. + // This is not done per single argument value, because this could cause performance problems. + $row_args = array(); + + foreach ($vars['rows'] as $id => $row) { + $row_args[$id] = $argument->summary_argument($row); + } + $argument->process_summary_arguments($row_args); + + foreach ($vars['rows'] as $id => $row) { + $vars['row_classes'][$id] = ''; + + $vars['rows'][$id]->link = $argument->summary_name($row); + $args = $view->args; + $args[$argument->position] = $row_args[$id]; + + $base_path = NULL; + if (!empty($argument->options['summary_options']['base_path'])) { + $base_path = $argument->options['summary_options']['base_path']; + } + $vars['rows'][$id]->url = url($view->getUrl($args, $base_path), $url_options); + $vars['rows'][$id]->count = intval($row->{$argument->count_alias}); + if (isset($active_urls[$vars['rows'][$id]->url])) { + $vars['row_classes'][$id] = 'active'; + } + } +} + +/** + * Template preprocess theme function to print summary basically + * unformatted. + */ +function template_preprocess_views_view_summary_unformatted(&$vars) { + $view = $vars['view']; + $argument = $view->argument[$view->build_info['summary_level']]; + $vars['row_classes'] = array(); + + $url_options = array(); + + if (!empty($view->exposed_raw_input)) { + $url_options['query'] = $view->exposed_raw_input; + } + + $count = 0; + $active_urls = drupal_map_assoc(array( + url(current_path(), array('alias' => TRUE)), // force system path + url(current_path()), // could be an alias + )); + + // Collect all arguments foreach row, to be able to alter them for example by the validator. + // This is not done per single argument value, because this could cause performance problems. + $row_args = array(); + foreach ($vars['rows'] as $id => $row) { + $row_args[$id] = $argument->summary_argument($row); + } + $argument->process_summary_arguments($row_args); + + foreach ($vars['rows'] as $id => $row) { + // only false on first time: + if ($count++) { + $vars['rows'][$id]->separator = filter_xss_admin($vars['options']['separator']); + } + $vars['rows'][$id]->link = $argument->summary_name($row); + $args = $view->args; + $args[$argument->position] = $row_args[$id]; + + $base_path = NULL; + if (!empty($argument->options['summary_options']['base_path'])) { + $base_path = $argument->options['summary_options']['base_path']; + } + $vars['rows'][$id]->url = url($view->getUrl($args, $base_path), $url_options); + $vars['rows'][$id]->count = intval($row->{$argument->count_alias}); + if (isset($active_urls[$vars['rows'][$id]->url])) { + $vars['row_classes'][$id] = 'active'; + } + } +} + +/** + * Display a view as a table style. + */ +function template_preprocess_views_view_table(&$vars) { + $view = $vars['view']; + + // We need the raw data for this grouping, which is passed in as $vars['rows']. + // However, the template also needs to use for the rendered fields. We + // therefore swap the raw data out to a new variable and reset $vars['rows'] + // so that it can get rebuilt. + // Store rows so that they may be used by further preprocess functions. + $result = $vars['result'] = $vars['rows']; + $vars['rows'] = array(); + $vars['field_classes'] = array(); + $vars['header'] = array(); + + $options = $view->style_plugin->options; + $handler = $view->style_plugin; + + $default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : TRUE; + $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : TRUE; + + $fields = &$view->field; + $columns = $handler->sanitize_columns($options['columns'], $fields); + + $active = !empty($handler->active) ? $handler->active : ''; + $order = !empty($handler->order) ? $handler->order : 'asc'; + + // A boolean variable which stores whether the table has a responsive class. + $responsive = FALSE; + + $query = tablesort_get_query_parameters(); + if (isset($view->exposed_raw_input)) { + $query += $view->exposed_raw_input; + } + + // Fields must be rendered in order as of Views 2.3, so we will pre-render + // everything. + $renders = $handler->render_fields($result); + + foreach ($columns as $field => $column) { + // Create a second variable so we can easily find what fields we have and what the + // CSS classes should be. + $vars['fields'][$field] = drupal_clean_css_identifier($field); + if ($active == $field) { + $vars['fields'][$field] .= ' active'; + } + + // render the header labels + if ($field == $column && empty($fields[$field]->options['exclude'])) { + $label = check_plain(!empty($fields[$field]) ? $fields[$field]->label() : ''); + if (empty($options['info'][$field]['sortable']) || !$fields[$field]->click_sortable()) { + $vars['header'][$field] = $label; + } + else { + $initial = !empty($options['info'][$field]['default_sort_order']) ? $options['info'][$field]['default_sort_order'] : 'asc'; + + if ($active == $field) { + $initial = ($order == 'asc') ? 'desc' : 'asc'; + } + + $title = t('sort by @s', array('@s' => $label)); + if ($active == $field) { + $label .= theme('tablesort_indicator', array('style' => $initial)); + } + + $query['order'] = $field; + $query['sort'] = $initial; + $link_options = array( + 'html' => TRUE, + 'attributes' => array('title' => $title), + 'query' => $query, + ); + $vars['header'][$field] = l($label, current_path(), $link_options); + } + + $vars['header_classes'][$field] = ''; + // Set up the header label class. + if ($fields[$field]->options['element_default_classes']) { + $vars['header_classes'][$field] .= "views-field views-field-" . $vars['fields'][$field]; + } + $class = $fields[$field]->element_label_classes(0); + if ($class) { + if ($vars['header_classes'][$field]) { + $vars['header_classes'][$field] .= ' '; + } + $vars['header_classes'][$field] .= $class; + } + // Add responsive header classes. + if (!empty($options['info'][$field]['responsive'])) { + $vars['header_classes'][$field] .= ' ' . $options['info'][$field]['responsive']; + $responsive = TRUE; + } + // Add a CSS align class to each field if one was set + if (!empty($options['info'][$field]['align'])) { + $vars['header_classes'][$field] .= ' ' . drupal_clean_css_identifier($options['info'][$field]['align']); + } + + // Add a header label wrapper if one was selected. + if ($vars['header'][$field]) { + $element_label_type = $fields[$field]->element_label_type(TRUE, TRUE); + if ($element_label_type) { + $vars['header'][$field] = '<' . $element_label_type . '>' . $vars['header'][$field] . '' . $element_label_type . '>'; + } + } + + } + + // Add a CSS align class to each field if one was set + if (!empty($options['info'][$field]['align'])) { + $vars['fields'][$field] .= ' ' . drupal_clean_css_identifier($options['info'][$field]['align']); + } + + // Render each field into its appropriate column. + foreach ($result as $num => $row) { + // Add field classes + $vars['field_classes'][$field][$num] = ''; + if ($fields[$field]->options['element_default_classes']) { + $vars['field_classes'][$field][$num] = "views-field views-field-" . $vars['fields'][$field]; + } + if ($classes = $fields[$field]->element_classes($num)) { + if ($vars['field_classes'][$field][$num]) { + $vars['field_classes'][$field][$num] .= ' '; + } + + $vars['field_classes'][$field][$num] .= $classes; + } + // Add responsive header classes. + if (!empty($options['info'][$field]['responsive'])) { + $vars['field_classes'][$field][$num] .= ' ' . $options['info'][$field]['responsive']; + } + + $vars['field_attributes'][$field][$num] = array(); + + if (!empty($fields[$field]) && empty($fields[$field]->options['exclude'])) { + $field_output = $renders[$num][$field]; + $element_type = $fields[$field]->element_type(TRUE, TRUE); + if ($element_type) { + $field_output = '<' . $element_type . '>' . $field_output . '' . $element_type . '>'; + } + + // Don't bother with separators and stuff if the field does not show up. + if (empty($field_output) && !empty($vars['rows'][$num][$column])) { + continue; + } + + // Place the field into the column, along with an optional separator. + if (!empty($vars['rows'][$num][$column])) { + if (!empty($options['info'][$column]['separator'])) { + $vars['rows'][$num][$column] .= filter_xss_admin($options['info'][$column]['separator']); + } + } + else { + $vars['rows'][$num][$column] = ''; + } + + $vars['rows'][$num][$column] .= $field_output; + } + } + + // Remove columns if the option is hide empty column is checked and the field is not empty. + if (!empty($options['info'][$field]['empty_column'])) { + $empty = TRUE; + foreach ($vars['rows'] as $num => $columns) { + $empty &= empty($columns[$column]); + } + if ($empty) { + foreach ($vars['rows'] as $num => &$column_items) { + unset($column_items[$column]); + unset($vars['header'][$column]); + } + } + } + } + + // Hide table header if all labels are empty. + if (!array_filter($vars['header'])) { + $vars['header'] = array(); + } + + $count = 0; + foreach ($vars['rows'] as $num => $row) { + $vars['row_classes'][$num] = array(); + if ($row_class_special) { + $vars['row_classes'][$num][] = ($count++ % 2 == 0) ? 'odd' : 'even'; + } + if ($row_class = $handler->get_row_class($num)) { + $vars['row_classes'][$num][] = $row_class; + } + } + + if ($row_class_special) { + $vars['row_classes'][0][] = 'views-row-first'; + $vars['row_classes'][count($vars['row_classes']) - 1][] = 'views-row-last'; + } + + $vars['attributes']['class'] = array('views-table'); + if (empty($vars['rows']) && !empty($options['empty_table'])) { + $vars['rows'][0][0] = $view->display_handler->renderArea('empty'); + // Calculate the amounts of rows with output. + $vars['field_attributes'][0][0]['colspan'] = count($vars['header']); + $vars['field_classes'][0][0] = 'views-empty'; + } + + if (!empty($options['sticky'])) { + drupal_add_js('misc/tableheader.js'); + $vars['attributes']['class'][] = "sticky-enabled"; + } + $vars['attributes']['class'][] = 'cols-'. count($vars['header']); + + if (!empty($handler->options['summary'])) { + $vars['attributes_array'] = array('summary' => $handler->options['summary']); + } + // If the table has headers and it should react responsively to columns hidden + // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM + // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors. + if (count($vars['header']) && $responsive) { + drupal_add_library('system', 'drupal.tableresponsive'); + // Add 'responsive-enabled' class to the table to identify it for JS. + // This is needed to target tables constructed by this function. + $vars['attributes']['class'][] = 'responsive-enabled'; + } +} + +/** + * Display a view as a grid style. + */ +function template_preprocess_views_view_grid(&$vars) { + $view = $vars['view']; + $result = $view->result; + $options = $view->style_plugin->options; + $handler = $view->style_plugin; + $default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : TRUE; + $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : TRUE; + + $columns = $options['columns']; + $vars['attributes']['class'][] = 'views-view-grid cols-' . $columns; + + $rows = array(); + $row_indexes = array(); + + if ($options['alignment'] == 'horizontal') { + $row = array(); + $col_count = 0; + $row_count = 0; + $count = 0; + foreach ($vars['rows'] as $row_index => $item) { + $count++; + $row[] = $item; + $row_indexes[$row_count][$col_count] = $row_index; + $col_count++; + if ($count % $columns == 0) { + $rows[] = $row; + $row = array(); + $col_count = 0; + $row_count++; + } + } + if ($row) { + // Fill up the last line only if it's configured, but this is default. + if (!empty($handler->options['fill_single_line']) && count($rows)) { + for ($i = 0; $i < ($columns - $col_count); $i++) { + $row[] = ''; + } + } + $rows[] = $row; + } + } + else { + $num_rows = floor(count($vars['rows']) / $columns); + // The remainders are the 'odd' columns that are slightly longer. + $remainders = count($vars['rows']) % $columns; + $row = 0; + $col = 0; + foreach ($vars['rows'] as $count => $item) { + $rows[$row][$col] = $item; + $row_indexes[$row][$col] = $count; + $row++; + + if (!$remainders && $row == $num_rows) { + $row = 0; + $col++; + } + elseif ($remainders && $row == $num_rows + 1) { + $row = 0; + $col++; + $remainders--; + } + } + for ($i = 0; $i < count($rows[0]); $i++) { + // This should be string so that's okay :) + if (!isset($rows[count($rows) - 1][$i])) { + $rows[count($rows) - 1][$i] = ''; + } + } + } + + // Apply the row classes + foreach ($rows as $row_number => $row) { + $row_classes = array(); + if ($default_row_class) { + $row_classes[] = 'row-' . ($row_number + 1); + } + if ($row_class_special) { + if ($row_number == 0) { + $row_classes[] = 'row-first'; + } + if (count($rows) == ($row_number + 1)) { + $row_classes[] = 'row-last'; + } + } + $vars['row_classes'][$row_number] = implode(' ', $row_classes); + foreach ($rows[$row_number] as $column_number => $item) { + $column_classes = array(); + if ($default_row_class) { + $column_classes[] = 'col-'. ($column_number + 1); + } + if ($row_class_special) { + if ($column_number == 0) { + $column_classes[] = 'col-first'; + } + elseif (count($rows[$row_number]) == ($column_number + 1)) { + $column_classes[] = 'col-last'; + } + } + if (isset($row_indexes[$row_number][$column_number]) && $column_class = $view->style_plugin->get_row_class($row_indexes[$row_number][$column_number])) { + $column_classes[] = $column_class; + } + $vars['column_classes'][$row_number][$column_number] = implode(' ', $column_classes); + } + } + $vars['rows'] = $rows; + if (!empty($handler->options['summary'])) { + $vars['attributes_array'] = array('summary' => $handler->options['summary']); + } + // If the table has headers and it should react responsively to columns hidden + // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM + // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors. + if (count($vars['header']) && $responsive) { + drupal_add_library('system', 'drupal.tableresponsive'); + // Add 'responsive-enabled' class to the table to identify it for JS. + // This is needed to target tables constructed by this function. + $vars['attributes']['class'][] = 'responsive-enabled'; + } +} + +/** + * Display the simple view of rows one after another + */ +function template_preprocess_views_view_unformatted(&$vars) { + $view = $vars['view']; + $rows = $vars['rows']; + $style = $view->style_plugin; + $options = $style->options; + + $vars['row_classes'] = array(); + $vars['classes'] = array(); + $default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : FALSE; + $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : FALSE; + // Set up striping values. + $count = 0; + $max = count($rows); + foreach ($rows as $id => $row) { + $count++; + if ($default_row_class) { + $vars['classes'][$id][] = 'views-row'; + $vars['classes'][$id][] = 'views-row-' . $count; + } + if ($row_class_special) { + $vars['classes'][$id][] = 'views-row-' . ($count % 2 ? 'odd' : 'even'); + if ($count == 1) { + $vars['classes'][$id][] = 'views-row-first'; + } + if ($count == $max) { + $vars['classes'][$id][] = 'views-row-last'; + } + } + + if ($row_class = $view->style_plugin->get_row_class($id)) { + $vars['classes'][$id][] = $row_class; + } + + // Flatten the classes to a string for each row for the template file. + $vars['row_classes'][$id] = isset($vars['classes'][$id]) ? implode(' ', $vars['classes'][$id]) : ''; + } +} + +/** + * Display the view as an HTML list element + */ +function template_preprocess_views_view_list(&$vars) { + $handler = $vars['view']->style_plugin; + + $class = explode(' ', $handler->options['class']); + $class = array_map('drupal_clean_css_identifier', $class); + + $wrapper_class = explode(' ', $handler->options['wrapper_class']); + $wrapper_class = array_map('drupal_clean_css_identifier', $wrapper_class); + + $vars['class'] = implode(' ', $class); + $vars['wrapper_class'] = implode(' ', $wrapper_class); + $vars['wrapper_prefix'] = ''; + $vars['wrapper_suffix'] = ''; + $vars['list_type_prefix'] = '<' . $handler->options['type'] . '>'; + $vars['list_type_suffix'] = '' . $handler->options['type'] . '>'; + if ($vars['wrapper_class']) { + $vars['wrapper_prefix'] = '' . $title . ''; + $output .= '' . $content . '' ; + $output .= ''; + $vars['wrapper_suffix'] = ''; + } + + if ($vars['class']) { + $vars['list_type_prefix'] = '<' . $handler->options['type'] . ' class="' . $vars['class'] . '">'; + } + template_preprocess_views_view_unformatted($vars); +} + +/** + * Preprocess an RSS feed + */ +function template_preprocess_views_view_rss(&$vars) { + global $base_url; + + $view = &$vars['view']; + $options = &$vars['options']; + $items = &$vars['rows']; + + $style = &$view->style_plugin; + + $config = config('system.site'); + + // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description. + // We strip all HTML tags, but need to prevent double encoding from properly + // escaped source data (such as & becoming &). + $vars['description'] = check_plain(decode_entities(strip_tags($style->get_description()))); + + if ($view->display_handler->getOption('sitename_title')) { + $title = $config->get('name'); + if ($slogan = $config->get('slogan')) { + $title .= ' - ' . $slogan; + } + } + else { + $title = $view->getTitle(); + } + $vars['title'] = check_plain($title); + + // Figure out which display which has a path we're using for this feed. If there isn't + // one, use the global $base_url + $link_display_id = $view->display_handler->getLinkDisplay(); + if ($link_display_id && !empty($view->displayHandlers[$link_display_id])) { + $path = $view->displayHandlers[$link_display_id]->getPath(); + } + + if ($path) { + $path = $view->getUrl(NULL, $path); + $url_options = array('absolute' => TRUE); + if (!empty($view->exposed_raw_input)) { + $url_options['query'] = $view->exposed_raw_input; + } + + // Compare the link to the default home page; if it's the default home page, just use $base_url. + if ($path == $config->get('page.front')) { + $path = ''; + } + + $vars['link'] = check_url(url($path, $url_options)); + } + + $vars['langcode'] = check_plain(language(LANGUAGE_TYPE_INTERFACE)->langcode); + $vars['namespaces'] = new Attribute($style->namespaces); + $vars['items'] = $items; + $vars['channel_elements'] = format_xml_elements($style->channel_elements); + + // During live preview we don't want to output the header since the contents + // of the feed are being displayed inside a normal HTML page. + if (empty($vars['view']->live_preview)) { + $vars['view']->getResponse()->headers->set('Content-Type', 'application/rss+xml; charset=utf-8'); + } +} + +/** + * Default theme function for all RSS rows. + */ +function template_preprocess_views_view_row_rss(&$vars) { + $view = &$vars['view']; + $options = &$vars['options']; + $item = &$vars['row']; + + $vars['title'] = check_plain($item->title); + $vars['link'] = check_url($item->link); + $vars['description'] = check_plain($item->description); + $vars['item_elements'] = empty($item->elements) ? '' : format_xml_elements($item->elements); +} + +/** + * Default theme function for all filter forms. + */ +function template_preprocess_views_exposed_form(&$vars) { + $form = &$vars['form']; + + // Put all single checkboxes together in the last spot. + $checkboxes = ''; + + if (!empty($form['q'])) { + $vars['q'] = drupal_render($form['q']); + } + + $vars['widgets'] = array(); + foreach ($form['#info'] as $id => $info) { + // Set aside checkboxes. + if (isset($form[$info['value']]['#type']) && $form[$info['value']]['#type'] == 'checkbox') { + $checkboxes .= drupal_render($form[$info['value']]); + continue; + } + $widget = new stdClass; + // set up defaults so that there's always something there. + $widget->label = $widget->operator = $widget->widget = $widget->description = NULL; + + $widget->id = isset($form[$info['value']]['#id']) ? $form[$info['value']]['#id'] : ''; + + if (!empty($info['label'])) { + $widget->label = check_plain($info['label']); + } + if (!empty($info['operator'])) { + $widget->operator = drupal_render($form[$info['operator']]); + } + + $widget->widget = drupal_render($form[$info['value']]); + + if (!empty($info['description'])) { + $widget->description = check_plain($info['description']); + } + + $vars['widgets'][$id] = $widget; + } + + // Wrap up all the checkboxes we set aside into a widget. + if ($checkboxes) { + $widget = new stdClass; + // set up defaults so that there's always something there. + $widget->label = $widget->operator = $widget->widget = NULL; + $widget->id = 'checkboxes'; + $widget->widget = $checkboxes; + $vars['widgets']['checkboxes'] = $widget; + } + + if (isset($form['sort_by'])) { + $vars['sort_by'] = drupal_render($form['sort_by']); + $vars['sort_order'] = drupal_render($form['sort_order']); + } + if (isset($form['items_per_page'])) { + $vars['items_per_page'] = drupal_render($form['items_per_page']); + } + if (isset($form['offset'])) { + $vars['offset'] = drupal_render($form['offset']); + } + if (isset($form['reset'])) { + $vars['reset_button'] = drupal_render($form['reset']); + } + // This includes the submit button. + $vars['button'] = drupal_render_children($form); +} + +/** + * Theme function for a View with form elements: replace the placeholders. + */ +function theme_views_form_views_form($variables) { + $form = $variables['form']; + + // Placeholders and their substitutions (usually rendered form elements). + $search = array(); + $replace = array(); + + // Add in substitutions provided by the form. + foreach ($form['#substitutions']['#value'] as $substitution) { + $field_name = $substitution['field_name']; + $row_id = $substitution['row_id']; + + $search[] = $substitution['placeholder']; + $replace[] = isset($form[$field_name][$row_id]) ? drupal_render($form[$field_name][$row_id]) : ''; + } + // Add in substitutions from hook_views_form_substitutions(). + $substitutions = module_invoke_all('views_form_substitutions'); + foreach ($substitutions as $placeholder => $substitution) { + $search[] = $placeholder; + $replace[] = $substitution; + } + + // Apply substitutions to the rendered output. + $form['output']['#markup'] = str_replace($search, $replace, $form['output']['#markup']); + + // Render and add remaining form fields. + return drupal_render_children($form); +} + +function theme_views_mini_pager($vars) { + global $pager_page_array, $pager_total; + + $tags = $vars['tags']; + $element = $vars['element']; + $parameters = $vars['parameters']; + $quantity = $vars['quantity']; + + // Calculate various markers within this pager piece: + // Middle is used to "center" pages around the current page. + $pager_middle = ceil($quantity / 2); + // current is the page we are currently paged to + $pager_current = $pager_page_array[$element] + 1; + // max is the maximum page number + $pager_max = $pager_total[$element]; + // End of marker calculations. + + if ($pager_total[$element] > 1) { + + $li_previous = theme('pager_previous', + array( + 'text' => (isset($tags[1]) ? $tags[1] : t('‹‹')), + 'element' => $element, + 'interval' => 1, + 'parameters' => $parameters, + ) + ); + if (empty($li_previous)) { + $li_previous = " "; + } + + $li_next = theme('pager_next', + array( + 'text' => (isset($tags[3]) ? $tags[3] : t('››')), + 'element' => $element, + 'interval' => 1, + 'parameters' => $parameters, + ) + ); + + if (empty($li_next)) { + $li_next = " "; + } + + $items[] = array( + 'class' => array('pager-previous'), + 'data' => $li_previous, + ); + + $items[] = array( + 'class' => array('pager-current'), + 'data' => t('@current of @max', array('@current' => $pager_current, '@max' => $pager_max)), + ); + + $items[] = array( + 'class' => array('pager-next'), + 'data' => $li_next, + ); + return theme('item_list', + array( + 'items' => $items, + 'title' => NULL, + 'type' => 'ul', + 'attributes' => array('class' => array('pager')), + ) + ); + } +} + +/** + * @defgroup views_templates Views template files + * @{ + * All views templates can be overridden with a variety of names, using + * the view, the display ID of the view, the display type of the view, + * or some combination thereof. + * + * For each view, there will be a minimum of two templates used. The first + * is used for all views: views-view.tpl.php. + * + * The second template is determined by the style selected for the view. Note + * that certain aspects of the view can also change which style is used; for + * example, arguments which provide a summary view might change the style to + * one of the special summary styles. + * + * The default style for all views is views-view-unformatted.tpl.php + * + * Many styles will then farm out the actual display of each row to a row + * style; the default row style is views-view-fields.tpl.php. + * + * Here is an example of all the templates that will be tried in the following + * case: + * + * View, named foobar. Style: unformatted. Row style: Fields. Display: Page. + * + * - views-view--foobar--page.tpl.php + * - views-view--page.tpl.php + * - views-view--foobar.tpl.php + * - views-view.tpl.php + * + * - views-view-unformatted--foobar--page.tpl.php + * - views-view-unformatted--page.tpl.php + * - views-view-unformatted--foobar.tpl.php + * - views-view-unformatted.tpl.php + * + * - views-view-fields--foobar--page.tpl.php + * - views-view-fields--page.tpl.php + * - views-view-fields--foobar.tpl.php + * - views-view-fields.tpl.php + * + * Important! When adding a new template to your theme, be sure to flush the + * theme registry cache! + * + * @see _views_theme_functions() + * @} + */ diff --git a/core/modules/views/theme/views-exposed-form.tpl.php b/core/modules/views/theme/views-exposed-form.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..bdd570c228a87c14ccf02d22a2f40d31936bb4e8 --- /dev/null +++ b/core/modules/views/theme/views-exposed-form.tpl.php @@ -0,0 +1,80 @@ +label: The visible label to print. May be optional. + * - $widget->operator: The operator for the widget. May be optional. + * - $widget->widget: The widget itself. + * - $sort_by: The select box to sort the view using an exposed form. + * - $sort_order: The select box with the ASC, DESC options to define order. May be optional. + * - $items_per_page: The select box with the available items per page. May be optional. + * - $offset: A textfield to define the offset of the view. May be optional. + * - $reset_button: A button to reset the exposed filter applied. May be optional. + * - $button: The submit button for the form. + * + * @ingroup views_templates + */ +?> + + + ++diff --git a/core/modules/views/theme/views-more.tpl.php b/core/modules/views/theme/views-more.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..0b7080bc54ce4b788b9272a76d99d5991023162e --- /dev/null +++ b/core/modules/views/theme/views-more.tpl.php @@ -0,0 +1,19 @@ + + + ++ + + +diff --git a/core/modules/views/theme/views-view-field.tpl.php b/core/modules/views/theme/views-view-field.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..91d92eee9541a6c184232313edf40382d0b0e65d --- /dev/null +++ b/core/modules/views/theme/views-view-field.tpl.php @@ -0,0 +1,25 @@ +{$field->field_alias} + * + * The above will guarantee that you'll always get the correct data, + * regardless of any changes in the aliasing that might happen if + * the view is modified. + */ +?> + diff --git a/core/modules/views/theme/views-view-fields.tpl.php b/core/modules/views/theme/views-view-fields.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..ae3a4c68c51e04e20b417a2fb0268e921ffa34d3 --- /dev/null +++ b/core/modules/views/theme/views-view-fields.tpl.php @@ -0,0 +1,36 @@ +content: The output of the field. + * - $field->raw: The raw data for the field, if it exists. This is NOT output safe. + * - $field->class: The safe class id to use. + * - $field->handler: The Views field handler object controlling this field. Do not use + * var_export to dump this object, as it can't handle the recursion. + * - $field->inline: Whether or not the field should be inline. + * - $field->inline_html: either div or span based on the above flag. + * - $field->wrapper_prefix: A complete wrapper containing the inline_html to use. + * - $field->wrapper_suffix: The closing tag for the wrapper. + * - $field->separator: an optional separator that may appear before a field. + * - $field->label: The wrap label text to use. + * - $field->label_html: The full HTML of the label to use including + * configured element type. + * - $row: The raw result object from the query, with all data it fetched. + * + * @ingroup views_templates + */ +?> + $field): ?> + separator)): ?> + separator; ?> + + + wrapper_prefix; ?> + label_html; ?> + content; ?> + wrapper_suffix; ?> + diff --git a/core/modules/views/theme/views-view-grid.tpl.php b/core/modules/views/theme/views-view-grid.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..603eed5e37d781573d43ec14af40f4d24bd7b598 --- /dev/null +++ b/core/modules/views/theme/views-view-grid.tpl.php @@ -0,0 +1,28 @@ + + + + +> + + $columns): ?> +
diff --git a/core/modules/views/theme/views-view-grouping.tpl.php b/core/modules/views/theme/views-view-grouping.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..ebf7bc2e3d74a1cb454f1ad7ca5d316d25a20ad6 --- /dev/null +++ b/core/modules/views/theme/views-view-grouping.tpl.php @@ -0,0 +1,25 @@ + +> + $item): ?> + + + +> + + + ++ +diff --git a/core/modules/views/theme/views-view-list.tpl.php b/core/modules/views/theme/views-view-list.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..2c4b9c5ecc6d73de0e809fd08b16cd6a3a6e6831 --- /dev/null +++ b/core/modules/views/theme/views-view-list.tpl.php @@ -0,0 +1,21 @@ + + + + + + + $row): ?> ++ ++> + + + diff --git a/core/modules/views/theme/views-view-row-comment.tpl.php b/core/modules/views/theme/views-view-row-comment.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..7fe2e81fe944783fb351db5baf06c57e530a83d7 --- /dev/null +++ b/core/modules/views/theme/views-view-row-comment.tpl.php @@ -0,0 +1,18 @@ + + diff --git a/core/modules/views/theme/views-view-row-rss.tpl.php b/core/modules/views/theme/views-view-row-rss.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..01e0696dc04159fe5b6a0a036f3e03607bfb26f5 --- /dev/null +++ b/core/modules/views/theme/views-view-row-rss.tpl.php @@ -0,0 +1,15 @@ + +- +
diff --git a/core/modules/views/theme/views-view-rss.tpl.php b/core/modules/views/theme/views-view-rss.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..18ca73eaff648300e117f96222954f6cc43532b1 --- /dev/null +++ b/core/modules/views/theme/views-view-rss.tpl.php @@ -0,0 +1,20 @@ + + version="1.0" encoding="utf-8" "; ?> ++ + + + > + diff --git a/core/modules/views/theme/views-view-summary-unformatted.tpl.php b/core/modules/views/theme/views-view-summary-unformatted.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..306d76fefae853566d33246d05dbd442f36dd4e8 --- /dev/null +++ b/core/modules/views/theme/views-view-summary-unformatted.tpl.php @@ -0,0 +1,20 @@ + + $row): ?> + '; ?> + separator)) { print $row->separator; } ?> + >link; ?> + + (count; ?>) + + ' : '+ ++ + + + + +
> + + | + +
---|
> + + | + +