/** * {@inheritdoc} */ public function build(RouteMatchInterface $route_match) { $breadcrumb = new Breadcrumb(); $links[] = Link::createFromRoute(t('Home'), '<front>'); // Articles page is a view. $links[] = Link::createFromRoute(t('Articles'), 'view.articles.page_1'); $links[] = Link::createFromRoute($this->node->label(), '<none>'); $breadcrumb->setLinks($links); return $breadcrumb; }
/** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form['#title'] = $this->entity->label(); if (!isset($this->entity->book)) { // The node is not part of any book yet - set default options. $this->entity->book = $this->bookManager->getLinkDefaults($this->entity->id()); } else { $this->entity->book['original_bid'] = $this->entity->book['bid']; } // Find the depth limit for the parent select. if (!isset($this->entity->book['parent_depth_limit'])) { $this->entity->book['parent_depth_limit'] = $this->bookManager->getParentDepthLimit($this->entity->book); } $form = $this->bookManager->addFormElements($form, $form_state, $this->entity, $this->currentUser(), FALSE); return $form; }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) { $form['#title'] = $node->label(); $form['#node'] = $node; $this->bookAdminTable($node, $form); $form['save'] = array('#type' => 'submit', '#value' => $this->t('Save book pages')); return $form; }
/** * Verifies that you can disable individual search plugins. */ function testSearchModuleDisabling() { // Array of search plugins to test: 'keys' are the keywords to search for, // and 'text' is the text to assert is on the results page. $plugin_info = array('node_search' => array('keys' => 'pizza', 'text' => $this->searchNode->label()), 'user_search' => array('keys' => $this->searchUser->getUsername(), 'text' => $this->searchUser->getEmail()), 'dummy_search_type' => array('keys' => 'foo', 'text' => 'Dummy search snippet to display')); $plugins = array_keys($plugin_info); /** @var $entities \Drupal\search\SearchPageInterface[] */ $entities = entity_load_multiple('search_page'); // Disable all of the search pages. foreach ($entities as $entity) { $entity->disable()->save(); } // Test each plugin if it's enabled as the only search plugin. foreach ($entities as $entity_id => $entity) { // Set this as default. $this->drupalGet("admin/config/search/pages/manage/{$entity_id}/set-default"); // Run a search from the correct search URL. $info = $plugin_info[$entity_id]; $this->drupalGet('search/' . $entity->getPath(), array('query' => array('keys' => $info['keys']))); $this->assertResponse(200); $this->assertNoText('no results', $entity->label() . ' search found results'); $this->assertText($info['text'], 'Correct search text found'); // Verify that other plugin search tab labels are not visible. foreach ($plugins as $other) { if ($other != $entity_id) { $label = $entities[$other]->label(); $this->assertNoText($label, $label . ' search tab is not shown'); } } // Run a search from the search block on the node page. Verify you get // to this plugin's search results page. $terms = array('keys' => $info['keys']); $this->submitGetForm('node', $terms, t('Search')); $current = $this->getURL(); $expected = \Drupal::url('search.view_' . $entity->id(), array(), array('query' => array('keys' => $info['keys']), 'absolute' => TRUE)); $this->assertEqual($current, $expected, 'Block redirected to right search page'); // Try an invalid search path, which should 404. $this->drupalGet('search/not_a_plugin_path'); $this->assertResponse(404); $entity->disable()->save(); } // Test with all search plugins enabled. When you go to the search // page or run search, all plugins should be shown. foreach ($entities as $entity) { $entity->enable()->save(); } // Set the node search as default. $this->drupalGet('admin/config/search/pages/manage/node_search/set-default'); $paths = array(array('path' => 'search/node', 'options' => array('query' => array('keys' => 'pizza'))), array('path' => 'search/node', 'options' => array())); foreach ($paths as $item) { $this->drupalGet($item['path'], $item['options']); foreach ($plugins as $entity_id) { $label = $entities[$entity_id]->label(); $this->assertText($label, format_string('%label search tab is shown', array('%label' => $label))); } } }
/** * Generates HTML for export when invoked by book_export(). * * The given node is embedded to its absolute depth in a top level section. For * example, a child node with depth 2 in the hierarchy is contained in * (otherwise empty) <div> elements corresponding to depth 0 and depth 1. * This is intended to support WYSIWYG output - e.g., level 3 sections always * look like level 3 sections, no matter their depth relative to the node * selected to be exported as printer-friendly HTML. * * @param \Drupal\node\NodeInterface $node * The node to export. * * @throws \Exception * Thrown when the node was not attached to a book. * * @return array * A render array representing the HTML for a node and its children in the * book hierarchy. */ public function bookExportHtml(NodeInterface $node) { if (!isset($node->book)) { throw new \Exception(); } $tree = $this->bookManager->bookSubtreeData($node->book); $contents = $this->exportTraverse($tree, array($this, 'bookNodeExport')); return array('#theme' => 'book_export_html', '#title' => $node->label(), '#contents' => $contents, '#depth' => $node->book['depth'], '#cache' => ['tags' => $node->getEntityType()->getListCacheTags()]); }
public function nodeGreeting(NodeInterface $node) { if ($node->isPublished()) { $formatted = $node->body->processed; foreach ($node->field_tags as $tag) { $terms[] = $tag->entity->label(); } return ['#theme' => 'greeting_node', '#title' => $node->label() . ' (' . $node->bundle() . ')', '#body' => $formatted, '#name' => $node->getOwner()->label(), '#terms' => $terms]; } return ['#markup' => $this->t('Not published')]; }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $this->nodeStorage->deleteRevision($this->revision->getRevisionId()); $this->logger('content')->notice('@type: deleted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId())); $node_type = $this->nodeTypeStorage->load($this->revision->bundle())->label(); drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($this->revision->getRevisionCreationTime()), '@type' => $node_type, '%title' => $this->revision->label()))); $form_state->setRedirect('entity.node.canonical', array('node' => $this->revision->id())); if ($this->connection->query('SELECT COUNT(DISTINCT vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $this->revision->id()))->fetchField() > 1) { $form_state->setRedirect('entity.node.version_history', array('node' => $this->revision->id())); } }
/** * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { $this->nodeStorage->deleteRevision($this->revision->getRevisionId()); watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId())); $node_type = $this->nodeTypeStorage->load($this->revision->bundle())->label(); drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($this->revision->getRevisionCreationTime()), '@type' => $node_type, '%title' => $this->revision->label()))); $form_state['redirect_route'] = array('route_name' => 'node.view', 'route_parameters' => array('node' => $this->revision->id())); if ($this->connection->query('SELECT COUNT(DISTINCT vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $this->revision->id()))->fetchField() > 1) { $form_state['redirect_route']['route_name'] = 'node.revision_overview'; } }
/** * Tests the administrative listing of all book pages in a book. */ public function testAdminBookNodeListing() { // Create a new book. $this->createBook(); $this->drupalLogin($this->adminUser); // Load the book page list and assert the created book title is displayed // and action links are shown on list items. $this->drupalGet('admin/structure/book/' . $this->book->id()); $this->assertText($this->book->label(), 'The book title is displayed on the administrative book listing page.'); $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a'); $this->assertEqual((string) $elements[0], 'View', 'View link is found from the list.'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { // The revision timestamp will be updated when the revision is saved. Keep // the original one for the confirmation message. $original_revision_timestamp = $this->revision->getRevisionCreationTime(); $this->revision = $this->prepareRevertedRevision($this->revision, $form_state); $this->revision->revision_log = t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)]); $this->revision->save(); $this->logger('content')->notice('@type: reverted %title revision %revision.', ['@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()]); drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', ['@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => $this->dateFormatter->format($original_revision_timestamp)])); $form_state->setRedirect('entity.node.version_history', array('node' => $this->revision->id())); }
/** * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { $this->revision->setNewRevision(); // Make this the new default revision for the node. $this->revision->isDefaultRevision(TRUE); // The revision timestamp will be updated when the revision is saved. Keep the // original one for the confirmation message. $original_revision_timestamp = $this->revision->getRevisionCreationTime(); $this->revision->revision_log = t('Copy of the revision from %date.', array('%date' => format_date($original_revision_timestamp))); $this->revision->save(); watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId())); drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array('@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => format_date($original_revision_timestamp)))); $form_state['redirect_route'] = array('route_name' => 'node.revision_overview', 'route_parameters' => array('node' => $this->revision->id())); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $this->revision->setNewRevision(); // Make this the new default revision for the node. $this->revision->isDefaultRevision(TRUE); // The revision timestamp will be updated when the revision is saved. Keep the // original one for the confirmation message. $original_revision_timestamp = $this->revision->getRevisionCreationTime(); $this->revision->revision_log = t('Copy of the revision from %date.', array('%date' => format_date($original_revision_timestamp))); $this->revision->save(); $this->logger('content')->notice('@type: reverted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId())); drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', array('@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => format_date($original_revision_timestamp)))); $form_state->setRedirect('entity.node.version_history', array('node' => $this->revision->id())); }
/** * Tests that the embedded form appears and can be submitted. */ function testEmbeddedForm() { // First verify we can submit the form from the module's page. $this->drupalPostForm('search_embedded_form', array('name' => 'John'), t('Send away')); $this->assertText(t('Test form was submitted'), 'Form message appears'); $count = \Drupal::state()->get('search_embedded_form.submit_count'); $this->assertEqual($this->submitCount + 1, $count, 'Form submission count is correct'); $this->submitCount = $count; // Now verify that we can see and submit the form from the search results. $this->drupalGet('search/node', array('query' => array('keys' => $this->node->label()))); $this->assertText(t('Your name'), 'Form is visible'); $this->drupalPostForm(NULL, array('name' => 'John'), t('Send away')); $this->assertText(t('Test form was submitted'), 'Form message appears'); $count = \Drupal::state()->get('search_embedded_form.submit_count'); $this->assertEqual($this->submitCount + 1, $count, 'Form submission count is correct'); $this->submitCount = $count; // Now verify that if we submit the search form, it doesn't count as // our form being submitted. $this->drupalPostForm('search', array('keys' => 'foo'), t('Search')); $this->assertNoText(t('Test form was submitted'), 'Form message does not appear'); $count = \Drupal::state()->get('search_embedded_form.submit_count'); $this->assertEqual($this->submitCount, $count, 'Form submission count is correct'); $this->submitCount = $count; }
/** * Test the flag relationship on another relationship. */ public function doContentView() { $this->drupalLogin($this->userA); $this->drupalGet('flag-followers/content'); $this->assertText($this->nodeB->label()); $this->assertNoText($this->nodeC->label()); $this->drupalLogin($this->userB); $this->drupalGet('flag-followers/content'); $this->assertText($this->nodeA->label()); $this->assertText($this->nodeC->label()); $this->drupalLogin($this->userC); $this->drupalGet('flag-followers/content'); $this->assertNoText($this->nodeA->label()); $this->assertNoText($this->nodeB->label()); }
/** * Tests the book navigation block when book is unpublished. * * There was a fatal error with "Show block only on book pages" block mode. */ public function testBookNavigationBlockOnUnpublishedBook() { // Create a new book. $this->createBook(); // Create administrator user. $administratorUser = $this->drupalCreateUser(['administer blocks', 'administer nodes', 'bypass node access']); $this->drupalLogin($administratorUser); // Enable the block with "Show block only on book pages" mode. $this->drupalPlaceBlock('book_navigation', ['block_mode' => 'book pages']); // Unpublish book node. $edit = []; $this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save and unpublish')); // Test node page. $this->drupalGet('node/' . $this->book->id()); $this->assertText($this->book->label(), 'Unpublished book with "Show block only on book pages" book navigation settings.'); }
public function nodeHug(NodeInterface $node) { if ($node->isPublished()) { // These are the same! $body = $node->body->value; $body = $node->body[0]->value; // But we really want... $formatted = $node->body->processed; $terms = []; foreach ($node->field_tags as $tag) { $terms[] = $tag->entity->label(); } $message = $this->t('Everyone hug @name because @reasons!', ['@name' => $node->getOwner()->label(), '@reasons' => implode(', ', $terms)]); return ['#title' => $node->label() . ' (' . $node->bundle() . ')', '#markup' => $message . $formatted]; } return $this->t('Not published'); }
/** * Update search index and search for comment. */ function assertCommentAccess($assume_access, $message) { // Invoke search index update. search_mark_for_reindex('node_search', $this->node->id()); $this->cronRun(); // Search for the comment subject. $edit = array('keys' => "'" . $this->commentSubject . "'"); $this->drupalPostForm('search/node', $edit, t('Search')); if ($assume_access) { $expected_node_result = $this->assertText($this->node->label()); $expected_comment_result = $this->assertText($this->commentSubject); } else { $expected_node_result = $this->assertText(t('Your search yielded no results.')); $expected_comment_result = $this->assertText(t('Your search yielded no results.')); } $this->assertTrue($expected_node_result && $expected_comment_result, $message); }
/** * Discussed at 47:00 in https://www.youtube.com/watch?v=8vwC_01KFLo * @param NodeInterface $node * @return type */ public function nodeMyHug(NodeInterface $node) { if ($node->isPublished()) { // These are the same! BUT DO NOT USE! (See below) $body = $node->body->value; // works even when body is multi-valued (gets the first one) $body = $node->body[0]->value; // works even when body is single-valued (gets the only one) // But we really want the processed value, which has been run through drupal's filters $formatted = $node->body->processed; $terms = []; foreach ($node->field_tags as $tag) { $terms[] = $tag->entity->label(); } $message = $this->t('Everyone give @name a my_hug because @reasons!', ['@name' => $node->getOwner()->label(), '@reasons' => implode(', ', $terms)]); return ['#title' => $node->label() . ' (' . $node->bundle() . ')', '#markup' => $message . $formatted]; } return $this->t('Not published'); }
/** * Tests advanced search by node type. */ function testNodeType() { // Verify some properties of the node that was created. $this->assertTrue($this->node->getType() == 'page', 'Node type is Basic page.'); $dummy_title = 'Lorem ipsum'; $this->assertNotEqual($dummy_title, $this->node->label(), "Dummy title doesn't equal node title."); // Search for the dummy title with a GET query. $this->drupalGet('search/node', array('query' => array('keys' => $dummy_title))); $this->assertNoText($this->node->label(), 'Basic page node is not found with dummy title.'); // Search for the title of the node with a GET query. $this->drupalGet('search/node', array('query' => array('keys' => $this->node->label()))); $this->assertText($this->node->label(), 'Basic page node is found with GET query.'); // Search for the title of the node with a POST query. $edit = array('or' => $this->node->label()); $this->drupalPostForm('search/node', $edit, t('Advanced search')); $this->assertText($this->node->label(), 'Basic page node is found with POST query.'); // Search by node type. $this->drupalPostForm('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search')); $this->assertText($this->node->label(), 'Basic page node is found with POST query and type:page.'); $this->drupalPostForm('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search')); $this->assertText('search yielded no results', 'Article node is not found with POST query and type:article.'); }
/** * Tests that user only has access to the their own nodes. * * @param \Drupal\node\Entity\Node $host_node * The node of the type of ief_simple_single * @param int $child_count * The number of entity reference values in the "single" field. */ protected function checkEditAccess(NodeInterface $host_node, $child_count, $cardinality) { $other_user = $this->createUser(['edit own ief_test_custom content', 'edit any ief_simple_single content']); /** @var \Drupal\node\Entity\Node $first_child_node */ $first_child_node = $host_node->single[0]->entity; $first_child_node->setOwner($other_user); $first_child_node->save(); $this->drupalGet("node/{$host_node->id()}/edit"); $this->assertText($first_child_node->label()); $this->assertNoFieldByName('single[0][inline_entity_form][title][0][value]', NULL, 'Form of child node with no edit access is not found.'); // Check that the forms for other child nodes(if any) appear on the form. $delta = 1; while ($delta < $child_count) { /** @var \Drupal\node\Entity\Node $child_node */ $child_node = $host_node->single[$delta]->entity; $this->assertFieldByName("single[{$delta}][inline_entity_form][title][0][value]", $child_node->label(), 'Form of child node with edit access is found.'); $delta++; } // Check that there is NOT an extra "add" form when editing. $unexpected_item_number = $child_count; $this->assertNoFieldByName("single[{$unexpected_item_number}][inline_entity_form][title][0][value]", NULL, 'No empty "add" entity form is found on edit.'); if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { $next_item_number = $child_count; $this->drupalPostAjaxForm(NULL, [], 'single_add_more'); $this->assertFieldByName("single[{$next_item_number}][inline_entity_form][title][0][value]", NULL, "Item {$next_item_number} does appear after 'Add More' clicked"); // Make sure only 1 item is added. $unexpected_item_number = $next_item_number + 1; $this->assertNoFieldByName("single[{$unexpected_item_number}][inline_entity_form][title][0][value]", NULL, "Extra Item {$unexpected_item_number} is not added after 'Add More' clicked"); } // Now that we have confirmed the correct fields appear, lets update the // values and save them. We do not have access to form for delta 0 because // it is owned by another user. $delta = 1; $new_titles = []; $edit = []; // Loop through an update all child node titles. while ($delta < $child_count) { /** @var \Drupal\node\Entity\Node $child_node */ $child_node = $host_node->single[$delta]->entity; $new_titles[$delta] = $child_node->label() . ' - updated'; $edit["single[{$delta}][inline_entity_form][title][0][value]"] = $new_titles[$delta]; $delta++; } // If CARDINALITY_UNLIMITED then we should have 1 extra form open. if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { $new_titles[$delta] = 'Title for new child'; $edit["single[{$delta}][inline_entity_form][title][0][value]"] = $new_titles[$delta]; } $this->drupalPostForm(NULL, $edit, t('Save')); $this->assertText("IEF single simple {$host_node->label()} has been updated."); // Reset cache for nodes. $node_ids = [$host_node->id()]; foreach ($host_node->single as $item) { $node_ids[] = $item->entity->id(); } $this->nodeStorage->resetCache($node_ids); $host_node = $this->nodeStorage->load($host_node->id()); // Check that titles were updated. foreach ($new_titles as $delta => $new_title) { $child_node = $host_node->single[$delta]->entity; $this->assertEqual($child_node->label(), $new_title, "Child {$delta} node title updated"); } }
/** * Build the default links (Read more) for a node. * * @param \Drupal\node\NodeInterface $entity * The node object. * @param string $view_mode * A view mode identifier. * * @return array * An array that can be processed by drupal_pre_render_links(). */ protected static function buildLinks(NodeInterface $entity, $view_mode) { $links = array(); // Always display a read more link on teasers because we have no way // to know when a teaser view is different than a full view. if ($view_mode == 'teaser') { $node_title_stripped = strip_tags($entity->label()); $links['node-readmore'] = array('title' => t('Read more<span class="visually-hidden"> about @title</span>', array('@title' => $node_title_stripped)), 'url' => $entity->urlInfo(), 'language' => $entity->language(), 'html' => TRUE, 'attributes' => array('rel' => 'tag', 'title' => $node_title_stripped)); } return array('#theme' => 'links__node__node', '#links' => $links, '#attributes' => array('class' => array('links', 'inline'))); }
/** * {@inheritdoc} */ public function addFormElements(array $form, FormStateInterface $form_state, NodeInterface $node, AccountInterface $account, $collapsed = TRUE) { // If the form is being processed during the Ajax callback of our book bid // dropdown, then $form_state will hold the value that was selected. if ($form_state->hasValue('book')) { $node->book = $form_state->getValue('book'); } $form['book'] = array('#type' => 'details', '#title' => $this->t('Book outline'), '#weight' => 10, '#open' => !$collapsed, '#group' => 'advanced', '#attributes' => array('class' => array('book-outline-form')), '#attached' => array('library' => array('book/drupal.book')), '#tree' => TRUE); foreach (array('nid', 'has_children', 'original_bid', 'parent_depth_limit') as $key) { $form['book'][$key] = array('#type' => 'value', '#value' => $node->book[$key]); } $form['book']['pid'] = $this->addParentSelectFormElements($node->book); // @see \Drupal\book\Form\BookAdminEditForm::bookAdminTableTree(). The // weight may be larger than 15. $form['book']['weight'] = array('#type' => 'weight', '#title' => $this->t('Weight'), '#default_value' => $node->book['weight'], '#delta' => max(15, abs($node->book['weight'])), '#weight' => 5, '#description' => $this->t('Pages at a given level are ordered first by weight and then by title.')); $options = array(); $nid = !$node->isNew() ? $node->id() : 'new'; if ($node->id() && $nid == $node->book['original_bid'] && $node->book['parent_depth_limit'] == 0) { // This is the top level node in a maximum depth book and thus cannot be // moved. $options[$node->id()] = $node->label(); } else { foreach ($this->getAllBooks() as $book) { $options[$book['nid']] = $book['title']; } } if ($account->hasPermission('create new books') && ($nid == 'new' || $nid != $node->book['original_bid'])) { // The node can become a new book, if it is not one already. $options = array($nid => $this->t('- Create a new book -')) + $options; } if (!$node->book['bid']) { // The node is not currently in the hierarchy. $options = array(0 => $this->t('- None -')) + $options; } // Add a drop-down to select the destination book. $form['book']['bid'] = array('#type' => 'select', '#title' => $this->t('Book'), '#default_value' => $node->book['bid'], '#options' => $options, '#access' => (bool) $options, '#description' => $this->t('Your page will be a part of the selected book.'), '#weight' => -5, '#attributes' => array('class' => array('book-title-select')), '#ajax' => array('callback' => 'book_form_update', 'wrapper' => 'edit-book-plid-wrapper', 'effect' => 'fade', 'speed' => 'fast')); return $form; }
/** * Generates an overview table of older revisions of a node. * * @param \Drupal\node\NodeInterface $node * A node object. * * @return array * An array as expected by drupal_render(). */ public function revisionOverview(NodeInterface $node) { $account = $this->currentUser(); $node_storage = $this->entityManager()->getStorage('node'); $type = $node->getType(); $build = array(); $build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label())); $header = array($this->t('Revision'), $this->t('Operations')); $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update'); $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'); $rows = array(); $vids = $node_storage->revisionIds($node); foreach (array_reverse($vids) as $vid) { if ($revision = $node_storage->loadRevision($vid)) { $row = array(); $revision_author = $revision->uid->entity; if ($vid == $node->getRevisionId()) { $username = array('#theme' => 'username', '#account' => $revision_author); $row[] = array('data' => $this->t('!date by !username', array('!date' => $node->link($this->dateFormatter->format($revision->revision_timestamp->value, 'short')), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : ''), 'class' => array('revision-current')); $row[] = array('data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => array('revision-current')); } else { $username = array('#theme' => 'username', '#account' => $revision_author); $row[] = $this->t('!date by !username', array('!date' => $this->l($this->dateFormatter->format($revision->revision_timestamp->value, 'short'), new Url('entity.node.revision', array('node' => $node->id(), 'node_revision' => $vid))), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : ''); if ($revert_permission) { $links['revert'] = array('title' => $this->t('Revert'), 'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid])); } if ($delete_permission) { $links['delete'] = array('title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid])); } $row[] = array('data' => array('#type' => 'operations', '#links' => $links)); } $rows[] = $row; } } $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin'))); return $build; }
public function testEntityLanguage(NodeInterface $node) { return ['#markup' => $node->label()]; }
/** * Tests a simple site export import case. */ public function testExportImport() { // After installation there is no snapshot and nothing to import. $this->drupalGet('admin/config/development/configuration'); $this->assertNoText(t('Warning message')); $this->assertText(t('There are no configuration changes to import.')); $this->originalSlogan = $this->config('system.site')->get('slogan'); $this->newSlogan = $this->randomString(16); $this->assertNotEqual($this->newSlogan, $this->originalSlogan); $this->config('system.site')->set('slogan', $this->newSlogan)->save(); $this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan); // Create a content type. $this->contentType = $this->drupalCreateContentType(); // Create a field. $this->fieldName = Unicode::strtolower($this->randomMachineName()); $this->fieldStorage = entity_create('field_storage_config', array('field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => 'text')); $this->fieldStorage->save(); entity_create('field_config', array('field_storage' => $this->fieldStorage, 'bundle' => $this->contentType->id()))->save(); // Update the displays so that configuration does not change unexpectedly on // import. entity_get_form_display('node', $this->contentType->id(), 'default')->setComponent($this->fieldName, array('type' => 'text_textfield'))->save(); entity_get_display('node', $this->contentType->id(), 'full')->setComponent($this->fieldName)->save(); entity_get_display('node', $this->contentType->id(), 'default')->setComponent($this->fieldName)->save(); entity_get_display('node', $this->contentType->id(), 'teaser')->removeComponent($this->fieldName)->save(); $this->drupalGet('node/add/' . $this->contentType->id()); $this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed'); // Export the configuration. $this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export'); $this->tarball = $this->getRawContent(); $this->config('system.site')->set('slogan', $this->originalSlogan)->save(); $this->assertEqual($this->config('system.site')->get('slogan'), $this->originalSlogan); // Delete the custom field. $fields = FieldConfig::loadMultiple(); foreach ($fields as $field) { if ($field->getName() == $this->fieldName) { $field->delete(); } } $field_storages = FieldStorageConfig::loadMultiple(); foreach ($field_storages as $field_storage) { if ($field_storage->getName() == $this->fieldName) { $field_storage->delete(); } } $this->drupalGet('node/add/' . $this->contentType->id()); $this->assertNoFieldByName("{$this->fieldName}[0][value]", '', 'Widget is not displayed'); // Import the configuration. $filename = 'temporary://' . $this->randomMachineName(); file_put_contents($filename, $this->tarball); $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload'); // There is no snapshot yet because an import has never run. $this->assertNoText(t('Warning message')); $this->assertNoText(t('There are no configuration changes to import.')); $this->assertText($this->contentType->label()); $this->drupalPostForm(NULL, array(), 'Import all'); // After importing the snapshot has been updated an there are no warnings. $this->assertNoText(t('Warning message')); $this->assertText(t('There are no configuration changes to import.')); $this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan); $this->drupalGet('node/add'); $this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed'); $this->config('system.site')->set('slogan', $this->originalSlogan)->save(); $this->drupalGet('admin/config/development/configuration'); $this->assertText(t('Warning message')); $this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import.'); // Ensure the item is displayed as part of a list (to avoid false matches // on the rest of the page) and that the list markup is not escaped. $this->assertRaw('<li>system.site</li>'); // Remove everything from staging. The warning about differences between the // active and snapshot should no longer exist. \Drupal::service('config.storage.staging')->deleteAll(); $this->drupalGet('admin/config/development/configuration'); $this->assertNoText(t('Warning message')); $this->assertNoText('The following items in your active configuration have changes since the last import that may be lost on the next import.'); $this->assertText(t('There are no configuration changes to import.')); // Write a file to staging. The warning about differences between the // active and snapshot should now exist. /** @var \Drupal\Core\Config\StorageInterface $staging */ $staging = $this->container->get('config.storage.staging'); $data = $this->config('system.site')->get(); $data['slogan'] = 'in the face'; $this->copyConfig($this->container->get('config.storage'), $staging); $staging->write('system.site', $data); $this->drupalGet('admin/config/development/configuration'); $this->assertText(t('Warning message')); $this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import.'); // Ensure the item is displayed as part of a list (to avoid false matches // on the rest of the page) and that the list markup is not escaped. $this->assertRaw('<li>system.site</li>'); }
/** * Generates an overview table of older revisions of a node. * * @param \Drupal\node\NodeInterface $node * A node object. * * @return array * An array as expected by drupal_render(). */ public function revisionOverview(NodeInterface $node) { $account = $this->currentUser(); $node_storage = $this->entityManager()->getStorage('node'); $type = $node->getType(); $build = array(); $build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label())); $header = array($this->t('Revision'), $this->t('Operations')); $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update'); $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'); $rows = array(); $vids = $node_storage->revisionIds($node); foreach (array_reverse($vids) as $vid) { $revision = $node_storage->loadRevision($vid); $username = ['#theme' => 'username', '#account' => $revision->uid->entity]; // Use revision link to link to revisions that are not active. $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short'); if ($vid != $node->getRevisionId()) { $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid])); } else { $link = $node->link($date); } $row = []; $column = ['data' => ['#type' => 'inline_template', '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}', '#context' => ['date' => $link, 'username' => $this->renderer->renderPlain($username), 'message' => Xss::filter($revision->revision_log->value)]]]; // @todo Simplify once https://www.drupal.org/node/2334319 lands. $this->renderer->addCacheableDependency($column['data'], $username); $row[] = $column; if ($vid == $node->getRevisionId()) { $row[0]['class'] = ['revision-current']; $row[] = ['data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => ['revision-current']]; } else { $links = []; if ($revert_permission) { $links['revert'] = ['title' => $this->t('Revert'), 'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid])]; } if ($delete_permission) { $links['delete'] = ['title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid])]; } $row[] = ['data' => ['#type' => 'operations', '#links' => $links]]; } $rows[] = $row; } $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin'))); return $build; }
public function testEntityLanguage(NodeInterface $node) { $build = ['#markup' => $node->label()]; \Drupal::service('renderer')->addCacheableDependency($build, $node); return $build; }
/** * {@inheritdoc} */ public function getQuestion() { return $this->t('Are you sure you want to remove %title from the book hierarchy?', array('%title' => $this->node->label())); }
/** * Generates an overview table of older revisions of a node. * * @param \Drupal\node\NodeInterface $node * A node object. * * @return array * An array as expected by drupal_render(). */ public function revisionOverview(NodeInterface $node) { $account = $this->currentUser(); $langcode = $node->language()->getId(); $langname = $node->language()->getName(); $languages = $node->getTranslationLanguages(); $has_translations = count($languages) > 1; $node_storage = $this->entityManager()->getStorage('node'); $type = $node->getType(); $build['#title'] = $has_translations ? $this->t('@langname revisions for %title', ['@langname' => $langname, '%title' => $node->label()]) : $this->t('Revisions for %title', ['%title' => $node->label()]); $header = array($this->t('Revision'), $this->t('Operations')); $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update'); $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'); $rows = array(); $vids = $node_storage->revisionIds($node); $latest_revision = TRUE; foreach (array_reverse($vids) as $vid) { /** @var \Drupal\node\NodeInterface $revision */ $revision = $node_storage->loadRevision($vid); // Only show revisions that are affected by the language that is being // displayed. if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) { $username = ['#theme' => 'username', '#account' => $revision->getRevisionAuthor()]; // Use revision link to link to revisions that are not active. $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short'); if ($vid != $node->getRevisionId()) { $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid])); } else { $link = $node->link($date); } $row = []; $column = ['data' => ['#type' => 'inline_template', '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}', '#context' => ['date' => $link, 'username' => $this->renderer->renderPlain($username), 'message' => ['#markup' => $revision->revision_log->value, '#allowed_tags' => Xss::getHtmlTagList()]]]]; // @todo Simplify once https://www.drupal.org/node/2334319 lands. $this->renderer->addCacheableDependency($column['data'], $username); $row[] = $column; if ($latest_revision) { $row[] = ['data' => ['#prefix' => '<em>', '#markup' => $this->t('Current revision'), '#suffix' => '</em>']]; foreach ($row as &$current) { $current['class'] = ['revision-current']; } $latest_revision = FALSE; } else { $links = []; if ($revert_permission) { $links['revert'] = ['title' => $this->t('Revert'), 'url' => $has_translations ? Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) : Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid])]; } if ($delete_permission) { $links['delete'] = ['title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid])]; } $row[] = ['data' => ['#type' => 'operations', '#links' => $links]]; } $rows[] = $row; } } $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin'))); return $build; }
/** * Tests a simple site export import case. */ public function testExportImport() { // After installation there is no snapshot and nothing to import. $this->drupalGet('admin/config/development/configuration'); $this->assertNoText(t('Warning message')); $this->assertText(t('There are no configuration changes to import.')); $this->originalSlogan = $this->config('system.site')->get('slogan'); $this->newSlogan = $this->randomString(16); $this->assertNotEqual($this->newSlogan, $this->originalSlogan); $this->config('system.site')->set('slogan', $this->newSlogan)->save(); $this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan); // Create a content type. $this->contentType = $this->drupalCreateContentType(); // Create a field. $this->fieldName = Unicode::strtolower($this->randomMachineName()); $this->fieldStorage = entity_create('field_storage_config', array('field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => 'text')); $this->fieldStorage->save(); entity_create('field_config', array('field_storage' => $this->fieldStorage, 'bundle' => $this->contentType->id()))->save(); // Update the displays so that configuration does not change unexpectedly on // import. entity_get_form_display('node', $this->contentType->id(), 'default')->setComponent($this->fieldName, array('type' => 'text_textfield'))->save(); entity_get_display('node', $this->contentType->id(), 'full')->setComponent($this->fieldName)->save(); entity_get_display('node', $this->contentType->id(), 'default')->setComponent($this->fieldName)->save(); entity_get_display('node', $this->contentType->id(), 'teaser')->removeComponent($this->fieldName)->save(); $this->drupalGet('node/add/' . $this->contentType->id()); $this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed'); // Export the configuration. $this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export'); $this->tarball = $this->getRawContent(); $this->config('system.site')->set('slogan', $this->originalSlogan)->save(); $this->assertEqual($this->config('system.site')->get('slogan'), $this->originalSlogan); // Delete the custom field. $fields = FieldConfig::loadMultiple(); foreach ($fields as $field) { if ($field->field_name == $this->fieldName) { $field->delete(); } } $field_storages = FieldStorageConfig::loadMultiple(); foreach ($field_storages as $field_storage) { if ($field_storage->getName() == $this->fieldName) { $field_storage->delete(); } } $this->drupalGet('node/add/' . $this->contentType->id()); $this->assertNoFieldByName("{$this->fieldName}[0][value]", '', 'Widget is not displayed'); // Import the configuration. $filename = 'temporary://' . $this->randomMachineName(); file_put_contents($filename, $this->tarball); $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload'); // There is no snapshot yet because an import has never run. $this->assertNoText(t('Warning message')); $this->assertNoText(t('There are no configuration changes to import.')); $this->assertText($this->contentType->label()); $this->drupalPostForm(NULL, array(), 'Import all'); // After importing the snapshot has been updated an there are no warnings. $this->assertNoText(t('Warning message')); $this->assertText(t('There are no configuration changes to import.')); $this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan); $this->drupalGet('node/add'); $this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed'); $this->config('system.site')->set('slogan', $this->originalSlogan)->save(); $this->drupalGet('admin/config/development/configuration'); $this->assertText(t('Warning message')); $this->assertText('Your current configuration has changed. Changes to these configuration items will be lost on the next synchronization: system.site'); // Remove everything from staging. The warning about differences between the // active and snapshot should still exist. \Drupal::service('config.storage.staging')->deleteAll(); $this->drupalGet('admin/config/development/configuration'); $this->assertText(t('Warning message')); $this->assertText('Your current configuration has changed. Changes to these configuration items will be lost on the next synchronization: system.site'); $this->assertText(t('There are no configuration changes to import.')); }