diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Json.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Json.php new file mode 100644 index 0000000..61c5f47 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Json.php @@ -0,0 +1,246 @@ + ''); + + return $options; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::executeHookMenu(). + */ + public function executeHookMenu($callbacks) { + $items = array(); + + $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'), + ); + } + + return $items; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::execute(). + */ + public function execute() { + $this->view->build(); + + if (!empty($this->view->build_info['fail'])) { + throw new NotFoundHttpException(); + } + + if (!empty($this->view->build_info['denied'])) { + throw new AccessDeniedHttpException(); + } + + // Execute the view. + $this->view->execute(); + + return new JsonResponse($this->view->result); + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::preview(). + */ + function preview() { + $this->view->execute(); + + if (!empty($this->view->result)) { + $response = new JsonResponse($this->view->result); + return $response->getContent(); + } + + return ''; + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::optionsSummary(). + */ + public function optionsSummary(&$categories, &$options) { + parent::optionsSummary($categories, $options); + + $categories['json'] = array( + 'title' => t('Json 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), + ); + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::buildOptionsForm(). + */ + 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 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/%/json".'), + '#default_value' => $this->getOption('path'), + '#field_prefix' => '' . url(NULL, array('absolute' => TRUE)), + '#field_suffix' => '‎', + '#attributes' => array('dir' => 'ltr'), + ); + break; + } + } + + /** + * Overrides Drupal\views\Plugin\views\display\DisplayPluginBase::validateOptionsForm(). + */ + public function validateOptionsForm(&$form, &$form_state) { + parent::validateOptionsForm($form, $form_state); + switch ($form_state['section']) { + case 'path': + if (strpos($form_state['values']['path'], '$arg') !== FALSE) { + form_error($form['path'], t('"$arg" is no longer supported. Use % instead.')); + } + + if (strpos($form_state['values']['path'], '%') === 0) { + form_error($form['path'], t('"%" may not be used for the first segment of a path.')); + } + + // automatically remove '/' and trailing whitespace from path. + $form_state['values']['path'] = trim($form_state['values']['path'], '/ '); + 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 'path': + $this->setOption('path', $form_state['values']['path']); + break; + } + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayJsonTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayJsonTest.php new file mode 100644 index 0000000..3c0491a --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayJsonTest.php @@ -0,0 +1,60 @@ + 'Display: JSON plugin', + 'description' => 'Tests the JSON display plugin.', + 'group' => 'Views Plugins', + ); + } + + protected function setUp() { + parent::setUp(); + + for ($i = 1; $i <= 10; $i++) { + $this->drupalCreateNode(); + } + + $this->enableViewsTestModule(); + } + + /** + * Checks the behavior of JSON callback paths. + */ + public function testJsonResponses() { + $view = views_get_view('test_json_display'); + $view->initDisplay(); + $this->executeView($view); + + // Test the JSON callback. + $actual_json = $this->drupalGet('test/json'); + $this->assertResponse(200); + // Test the http content-type. + $headers = $this->drupalGetHeaders(); + $this->assertEqual($headers['content-type'], 'application/json', 'The header content-type is correct.'); + + $expected_json = json_encode($view->result); + $this->assertIdentical($actual_json, $expected_json, 'The expected JSON output was found.'); + + // Test a 403 callback. + $this->drupalGet('test/json/denied'); + $this->assertResponse(403); + } + +} diff --git a/core/modules/views/tests/views_test_config/config/views.view.test_json_display.yml b/core/modules/views/tests/views_test_config/config/views.view.test_json_display.yml new file mode 100644 index 0000000..1f086f6 --- /dev/null +++ b/core/modules/views/tests/views_test_config/config/views.view.test_json_display.yml @@ -0,0 +1,74 @@ +base_table: views_test_data +name: test_json_display +description: '' +tag: '' +human_name: 'Test JSON display' +core: 8.x +api_version: '3.0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: perm + options: + perm: 'view published content' + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + options: + items_per_page: '10' + style: + type: default + row: + type: fields + fields: + name: + id: name + table: views_test_data + field: name + label: '' + sorts: + created: + id: created + table: views_test_data + field: created + order: DESC + title: 'Test JSON' + arguments: { } + json_1: + display_plugin: json + id: json_1 + display_title: Json + position: '' + display_options: + defaults: + access: false + path: test/json + access: + type: none + json_2: + display_plugin: json + id: json_2 + display_title: 'Json - access denied' + position: '' + display_options: + defaults: + access: false + path: test/json/denied + access: + type: perm + options: + perm: 'administer views' +base_field: id +disabled: '0' +module: views +langcode: und