/** * Checks that unchecking 'Create new revision' works when editing a node. */ function testNodeFormSaveWithoutRevision() { $this->drupalLogin($this->editor); $node_storage = $this->container->get('entity.manager')->getStorage('node'); // Set page revision setting 'create new revision'. This will mean new // revisions are created by default when the node is edited. $type = NodeType::load('page'); $type->setNewRevision(TRUE); $type->save(); // Create the node. $node = $this->drupalCreateNode(); // Verify the checkbox is checked on the node edit form. $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked"); // Uncheck the create new revision checkbox and save the node. $edit = array('revision' => FALSE); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); // Load the node again and check the revision is the same as before. $node_storage->resetCache(array($node->id())); $node_revision = $node_storage->load($node->id(), TRUE); $this->assertEqual($node_revision->getRevisionId(), $node->getRevisionId(), "After an existing node is saved with 'Create new revision' unchecked, a new revision is not created."); // Verify the checkbox is checked on the node edit form. $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked"); // Submit the form without changing the checkbox. $edit = array(); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); // Load the node again and check the revision is different from before. $node_storage->resetCache(array($node->id())); $node_revision = $node_storage->load($node->id()); $this->assertNotEqual($node_revision->getRevisionId(), $node->getRevisionId(), "After an existing node is saved with 'Create new revision' checked, a new revision is created."); }
/** * Tests the deletion of a scheduled node. * * This tests if it is possible to delete a node that does not have a * publication date set, when scheduled publishing is required. * * @see https://drupal.org/node/1614880 */ public function testScheduledNodeDelete() { // Log in. $this->drupalLogin($this->adminUser); // Create a published and an unpublished node, both without scheduling. $published_node = $this->drupalCreateNode(['type' => 'page', 'status' => 1]); $unpublished_node = $this->drupalCreateNode(['type' => 'page', 'status' => 0]); // Make scheduled publishing and unpublishing required. $node_type = NodeType::load('page'); $node_type->setThirdPartySetting('scheduler', 'publish_required', TRUE); $node_type->setThirdPartySetting('scheduler', 'unpublish_required', TRUE); $node_type->save(); // Check that deleting the nodes does not throw form validation errors. ### @TODO Delete was a button in 7.x but a separate link node/<nid>/delete in 8.x ### Is the previous validation (that we had to avoid on delete) still done now in D8, given that there is no form? ### Maybe this test is not actually checking anything useful? Can it be altered to do something testable? $this->drupalGet('node/' . $published_node->id() . '/delete'); // Note that the text 'error message' is used in a header h2 html tag which // is normally made hidden from browsers but will be in the page source. // It is also good when testing for the absense of somthing to also test // for the presence of text, hence the second assertion for each check. $this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete a published node with no scheduling information.'); $this->assertRaw(t('Are you sure you want to delete the content'), 'The deletion warning message is shown immediately when trying to delete a published node with no scheduling information.'); $this->drupalGet('node/' . $unpublished_node->id() . '/delete'); $this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete an unpublished node with no scheduling information.'); $this->assertRaw(t('Are you sure you want to delete the content'), 'The deletion warning message is shown immediately when trying to delete an unpublished node with no scheduling information.'); }
/** * Tests Drupal 6 node type to Drupal 8 migration. */ public function testNodeType() { $migration = entity_load('migration', 'd6_node_type'); // Test the test_page content type. $node_type_page = NodeType::load('test_page'); $this->assertIdentical('test_page', $node_type_page->id(), 'Node type test_page loaded'); $this->assertIdentical(TRUE, $node_type_page->displaySubmitted()); $this->assertIdentical(FALSE, $node_type_page->isNewRevision()); $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_page->getPreviewMode()); $this->assertIdentical($migration->getIdMap()->lookupDestinationID(array('test_page')), array('test_page')); // Test we have a body field. $field = FieldConfig::loadByName('node', 'test_page', 'body'); $this->assertIdentical('This is the body field label', $field->getLabel(), 'Body field was found.'); // Test the test_story content type. $node_type_story = NodeType::load('test_story'); $this->assertIdentical('test_story', $node_type_story->id(), 'Node type test_story loaded'); $this->assertIdentical(TRUE, $node_type_story->displaySubmitted()); $this->assertIdentical(FALSE, $node_type_story->isNewRevision()); $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_story->getPreviewMode()); $this->assertIdentical($migration->getIdMap()->lookupDestinationID(array('test_story')), array('test_story')); // Test we don't have a body field. $field = FieldConfig::loadByName('node', 'test_story', 'body'); $this->assertIdentical(NULL, $field, 'No body field found'); // Test the test_event content type. $node_type_event = NodeType::load('test_event'); $this->assertIdentical('test_event', $node_type_event->id(), 'Node type test_event loaded'); $this->assertIdentical(TRUE, $node_type_event->displaySubmitted()); $this->assertIdentical(TRUE, $node_type_event->isNewRevision()); $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_event->getPreviewMode()); $this->assertIdentical($migration->getIdMap()->lookupDestinationID(array('test_event')), array('test_event')); // Test we have a body field. $field = FieldConfig::loadByName('node', 'test_event', 'body'); $this->assertIdentical('Body', $field->getLabel(), 'Body field was found.'); }
public function testRecreateEntity() { $type_name = Unicode::strtolower($this->randomMachineName(16)); $content_type = entity_create('node_type', array('type' => $type_name, 'name' => 'Node type one')); $content_type->save(); node_add_body_field($content_type); /** @var \Drupal\Core\Config\StorageInterface $active */ $active = $this->container->get('config.storage'); /** @var \Drupal\Core\Config\StorageInterface $sync */ $sync = $this->container->get('config.storage.sync'); $config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id(); $this->copyConfig($active, $sync); // Delete the content type. This will also delete a field storage, a field, // an entity view display and an entity form display. $content_type->delete(); $this->assertFalse($active->exists($config_name), 'Content type\'s old name does not exist active store.'); // Recreate with the same type - this will have a different UUID. $content_type = entity_create('node_type', array('type' => $type_name, 'name' => 'Node type two')); $content_type->save(); node_add_body_field($content_type); $this->configImporter->reset(); // A node type, a field, an entity view display and an entity form display // will be recreated. $creates = $this->configImporter->getUnprocessedConfiguration('create'); $deletes = $this->configImporter->getUnprocessedConfiguration('delete'); $this->assertEqual(5, count($creates), 'There are 5 configuration items to create.'); $this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.'); $this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.'); $this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.'); $this->configImporter->import(); // Verify that there is nothing more to import. $this->assertFalse($this->configImporter->reset()->hasUnprocessedConfigurationChanges()); $content_type = NodeType::load($type_name); $this->assertEqual('Node type one', $content_type->label()); }
/** * Checks that the Revision tab is displayed correctly. */ function testDisplayRevisionTab() { $this->drupalPlaceBlock('local_tasks_block'); $this->drupalLogin($this->editor); $node_storage = $this->container->get('entity.manager')->getStorage('node'); // Set page revision setting 'create new revision'. This will mean new // revisions are created by default when the node is edited. $type = NodeType::load('page'); $type->setNewRevision(TRUE); $type->save(); // Create the node. $node = $this->drupalCreateNode(); // Verify the checkbox is checked on the node edit form. $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked"); // Uncheck the create new revision checkbox and save the node. $edit = array('revision' => FALSE); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published'); $this->assertUrl($node->toUrl()); $this->assertNoLink(t('Revisions')); // Verify the checkbox is checked on the node edit form. $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked"); // Submit the form without changing the checkbox. $edit = array(); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published'); $this->assertUrl($node->toUrl()); $this->assertLink(t('Revisions')); }
protected function setUp() { parent::setUp(); node_access_test_add_field(NodeType::load('article')); node_access_rebuild(); \Drupal::state()->set('node_access_test.private', TRUE); }
/** * Tests if forum module uninstallation properly deletes the field. */ public function testForumUninstallWithField() { $this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'administer nodes', 'administer modules', 'delete any forum content', 'administer content types'])); // Ensure that the field exists before uninstallation. $field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums'); $this->assertNotNull($field_storage, 'The taxonomy_forums field storage exists.'); // Create a taxonomy term. $term = Term::create(['name' => t('A term'), 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(), 'description' => '', 'parent' => array(0), 'vid' => 'forums', 'forum_container' => 0]); $term->save(); // Create a forum node. $node = $this->drupalCreateNode(array('title' => 'A forum post', 'type' => 'forum', 'taxonomy_forums' => array(array('target_id' => $term->id())))); // Create at least one comment against the forum node. $comment = Comment::create(array('entity_id' => $node->nid->value, 'entity_type' => 'node', 'field_name' => 'comment_forum', 'pid' => 0, 'uid' => 0, 'status' => CommentInterface::PUBLISHED, 'subject' => $this->randomMachineName(), 'hostname' => '127.0.0.1')); $comment->save(); // Attempt to uninstall forum. $this->drupalGet('admin/modules/uninstall'); // Assert forum is required. $this->assertNoFieldByName('uninstall[forum]'); $this->assertText('To uninstall Forum, first delete all Forum content'); // Delete the node. $this->drupalPostForm('node/' . $node->id() . '/delete', array(), t('Delete')); // Attempt to uninstall forum. $this->drupalGet('admin/modules/uninstall'); // Assert forum is still required. $this->assertNoFieldByName('uninstall[forum]'); $this->assertText('To uninstall Forum, first delete all Forums terms'); // Delete any forum terms. $vid = $this->config('forum.settings')->get('vocabulary'); $terms = entity_load_multiple_by_properties('taxonomy_term', ['vid' => $vid]); foreach ($terms as $term) { $term->delete(); } // Ensure that the forum node type can not be deleted. $this->drupalGet('admin/structure/types/manage/forum'); $this->assertNoLink(t('Delete')); // Now attempt to uninstall forum. $this->drupalGet('admin/modules/uninstall'); // Assert forum is no longer required. $this->assertFieldByName('uninstall[forum]'); $this->drupalPostForm('admin/modules/uninstall', array('uninstall[forum]' => 1), t('Uninstall')); $this->drupalPostForm(NULL, [], t('Uninstall')); // Check that the field is now deleted. $field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums'); $this->assertNull($field_storage, 'The taxonomy_forums field storage has been deleted.'); // Check that a node type with a machine name of forum can be created after // uninstalling the forum module and the node type is not locked. $edit = array('name' => 'Forum', 'title_label' => 'title for forum', 'type' => 'forum'); $this->drupalPostForm('admin/structure/types/add', $edit, t('Save content type')); $this->assertTrue((bool) NodeType::load('forum'), 'Node type with machine forum created.'); $this->drupalGet('admin/structure/types/manage/forum'); $this->clickLink(t('Delete')); $this->drupalPostForm(NULL, array(), t('Delete')); $this->assertResponse(200); $this->assertFalse((bool) NodeType::load('forum'), 'Node type with machine forum deleted.'); // Double check everything by reinstalling the forum module again. $this->drupalPostForm('admin/modules', ['modules[Core][forum][enable]' => 1], 'Install'); $this->assertText('Module Forum has been enabled.'); }
protected function setUp() { parent::setUp(); node_access_rebuild(); $this->drupalCreateContentType(array('type' => 'page')); node_access_test_add_field(NodeType::load('page')); $this->addDefaultCommentField('node', 'page', 'comment', CommentItemInterface::OPEN); \Drupal::state()->set('node_access_test.private', TRUE); }
/** * Tests creating and editing nodes with required scheduling enabled. */ public function testRequiredScheduling() { $this->drupalLogin($this->adminUser); // Define test scenarios with expected results. $test_cases = [['id' => 0, 'required' => '', 'operation' => 'add', 'status' => 1, 'expected' => 'not required', 'message' => 'By default when a new node is created, the publish on and unpublish on dates are not required.'], ['id' => 1, 'required' => 'publish', 'operation' => 'add', 'status' => 0, 'expected' => 'required', 'message' => 'When scheduled publishing is required and a new unpublished node is created, entering a date in the publish on field is required.'], ['id' => 2, 'required' => 'publish', 'operation' => 'add', 'status' => 1, 'expected' => 'required', 'message' => 'When scheduled publishing is required and a new published node is created, entering a date in the publish on field is required.'], ['id' => 3, 'required' => 'publish', 'operation' => 'edit', 'scheduled' => 0, 'status' => 1, 'expected' => 'not required', 'message' => 'When scheduled publishing is required and an existing published, unscheduled node is edited, entering a date in the publish on field is not required.'], ['id' => 4, 'required' => 'publish', 'operation' => 'edit', 'scheduled' => 1, 'status' => 0, 'expected' => 'required', 'message' => 'When scheduled publishing is required and an existing unpublished, scheduled node is edited, entering a date in the publish on field is required.'], ['id' => 5, 'required' => 'publish', 'operation' => 'edit', 'scheduled' => 0, 'status' => 0, 'expected' => 'not required', 'message' => 'When scheduled publishing is required and an existing unpublished, unscheduled node is edited, entering a date in the publish on field is not required.'], ['id' => 6, 'required' => 'unpublish', 'operation' => 'add', 'status' => 0, 'expected' => 'required', 'message' => 'When scheduled unpublishing is required and a new unpublished node is created, entering a date in the unpublish on field is required.'], ['id' => 7, 'required' => 'unpublish', 'operation' => 'add', 'status' => 1, 'expected' => 'required', 'message' => 'When scheduled unpublishing is required and a new published node is created, entering a date in the unpublish on field is required.'], ['id' => 8, 'required' => 'unpublish', 'operation' => 'edit', 'scheduled' => 0, 'status' => 1, 'expected' => 'required', 'message' => 'When scheduled unpublishing is required and an existing published, unscheduled node is edited, entering a date in the unpublish on field is required.'], ['id' => 9, 'required' => 'unpublish', 'operation' => 'edit', 'scheduled' => 1, 'status' => 0, 'expected' => 'required', 'message' => 'When scheduled unpublishing is required and an existing unpublished, scheduled node is edited, entering a date in the unpublish on field is required.'], ['id' => 10, 'required' => 'unpublish', 'operation' => 'edit', 'scheduled' => 0, 'status' => 0, 'expected' => 'not required', 'message' => 'When scheduled unpublishing is required and an existing unpublished, unscheduled node is edited, entering a date in the unpublish on field is not required.']]; $node_type = NodeType::load('page'); $fields = \Drupal::entityManager()->getFieldDefinitions('node', 'page'); foreach ($test_cases as $test_case) { // Set required (un)publishing as stipulated by the test case. if (!empty($test_case['required'])) { $node_type->setThirdPartySetting('scheduler', 'publish_required', $test_case['required'] == 'publish')->setThirdPartySetting('scheduler', 'unpublish_required', $test_case['required'] == 'unpublish')->save(); } // To assist viewing and analysing the generated test result pages create // a text string showing all the test case parameters. $title_data = []; foreach ($test_case as $key => $value) { if ($key != 'message') { $title_data[] = $key . ' = ' . $value; } } $title = implode(', ', $title_data); // If the test case requires editing a node, we need to create one first. if ($test_case['operation'] == 'edit') { // Note: The key names in the $options parameter for drupalCreateNode() // are the plain field names i.e. 'title' not title[0][value] $options = ['title' => $title, 'type' => 'page', 'status' => $test_case['status'], 'publish_on' => !empty($test_case['scheduled']) ? strtotime('+1 day') : NULL]; $node = $this->drupalCreateNode($options); // Define the path and button to use for editing the node. $path = 'node/' . $node->id() . '/edit'; $button_text = $node->status->value ? t('Save and keep published') : t('Save and keep unpublished'); } else { // Set the default status, used when testing creation of the new node. $fields['status']->getConfig('page')->setDefaultValue($test_case['status'])->save(); // Define the path and button to use for creating the node. $path = 'node/add/page'; $button_text = t('Save and publish'); } // Make sure that both date fields are empty so we can check if they throw // validation errors when the fields are required. $edit = ['title[0][value]' => $title, 'publish_on[0][value][date]' => '', 'publish_on[0][value][time]' => '', 'unpublish_on[0][value][date]' => '', 'unpublish_on[0][value][time]' => '']; $this->drupalPostForm($path, $edit, $button_text); // Check for the expected result. switch ($test_case['expected']) { case 'required': $string = t('The %name date is required.', ['%name' => ucfirst($test_case['required']) . ' on']); $this->assertRaw($string, $test_case['id'] . '. ' . $test_case['message']); break; case 'not required': $string = '@type %title has been ' . ($test_case['operation'] == 'add' ? 'created' : 'updated') . '.'; $args = ['@type' => 'Basic page', '%title' => $title]; // @codingStandardsIgnoreStart $this->assertRaw(t($string, $args), $test_case['id'] . '. ' . $test_case['message']); // @codingStandardsIgnoreEnd break; } } }
/** * {@inheritdoc} */ protected function setUp() { parent::setUp(); // Create Article node type. $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); $this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('node'); node_access_test_add_field(NodeType::load('article')); // After enabling a node access module, the access table has to be rebuild. node_access_rebuild(); // Enable the private node feature of the node_access_test module. \Drupal::state()->set('node_access_test.private', TRUE); }
protected function setUp() { parent::setUp(); node_access_test_add_field(NodeType::load('page')); // After enabling a node access module, the access table has to be rebuild. node_access_rebuild(); // Enable the private node feature of the node_access_test module. \Drupal::state()->set('node_access_test.private', TRUE); // Add Hungarian, Catalan and Croatian. ConfigurableLanguage::createFromLangcode('hu')->save(); ConfigurableLanguage::createFromLangcode('ca')->save(); ConfigurableLanguage::createFromLangcode('hr')->save(); }
/** * Test event types in UI. */ function testEventType() { $web_user = $this->drupalCreateUser(['administer event types', 'access administration pages']); $this->drupalLogin($web_user); // Create and delete the testing event type. $event_bundle = $this->drupalCreateContentType(); $event_type = $this->createEventType($event_bundle); $this->drupalGet('admin/structure/rng/event_types/manage/' . $event_type->id() . '/edit'); $event_type->delete(); $event_bundle->delete(); // Event types button on admin. $this->drupalGet('admin/structure'); $this->assertLinkByHref(Url::fromRoute('rng.event_type.overview')->toString()); $this->assertRaw('Manage which entity bundles are designated as events.', 'Button shows in administration.'); // No events. $this->assertEqual(0, count(EventType::loadMultiple()), 'There are no event type entities.'); $this->drupalGet('admin/structure/rng/event_types'); $this->assertRaw('No event types found.', 'Event Type list is empty'); // There are no courier contexts. $this->assertEqual(0, count(CourierContext::loadMultiple()), 'There are no courier context entities.'); // Local action. $this->assertLinkByHref(Url::fromRoute('entity.event_type.add')->toString()); // Add. $t_args = ['%label' => 'node.event']; $edit = []; $this->drupalPostForm('admin/structure/rng/event_types/add', $edit, t('Save')); /** @var \Drupal\node\NodeTypeInterface $node_type */ $node_type = NodeType::load('event'); $this->assertEqual(1, count(EventType::loadMultiple()), 'Event type exists in database.'); $this->assertRaw(t('The content type <a href=":url">%label</a> has been added.', ['%label' => $node_type->label(), ':url' => $node_type->toUrl()->toString()]), 'Node was created for Event Type'); $this->assertRaw(t('%label event type added.', $t_args), 'Event Type created'); // Courier context created? $this->assertTrue(CourierContext::load('rng_registration_node'), 'Courier context entity created for this event type\' entity type.'); // Event type list. $this->assertUrl('admin/structure/rng/event_types', [], 'Browser was redirected to event type list.'); $this->assertRaw('<td>Content: event</td>', 'Event Type shows in list'); $options = ['node_type' => 'event']; $this->assertLinkByHref(Url::fromRoute("entity.node.field_ui_fields", $options)->toString()); // Edit form. $edit = []; $this->drupalPostForm('admin/structure/rng/event_types/manage/node.event/edit', $edit, t('Save')); $this->assertRaw(t('%label event type updated.', $t_args), 'Event Type edit form saved'); // Delete form. $this->drupalGet('admin/structure/rng/event_types/manage/node.event/delete'); $this->assertRaw('Are you sure you want to delete event type node.event?', 'Event Type delete form rendered.'); $this->drupalPostForm('admin/structure/rng/event_types/manage/node.event/delete', [], t('Delete')); $this->assertRaw(t('Event type %label was deleted.', $t_args), 'Event Type delete form saved'); $this->assertEqual(0, count(EventType::loadMultiple()), 'Event type deleted from database.'); // @todo: ensure conditional on form omits node/existing radios // @todo create event type with custom entity }
/** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { $node_types = simplenews_get_content_types(); $node_type = reset($node_types); if (count($node_types) == 1) { $label = NodeType::load($node_type)->label(); $this->derivatives[$node_type] = $base_plugin_definition; $this->derivatives[$node_type]['title'] = new TranslationWrapper('Add @label', array('@label' => $label)); $this->derivatives[$node_type]['route_parameters'] = array('node_type' => $node_type); } elseif (count($node_types) > 1) { $base_plugin_definition['route_name'] = 'node.add_page'; $base_plugin_definition['title'] = new TranslationWrapper('Add content'); $this->derivatives[] = $base_plugin_definition; } return parent::getDerivativeDefinitions($base_plugin_definition); }
/** * Tests a single node type. * * @dataProvider testNodeTypeDataProvider * * @param string $id * The node type ID. * @param string $label * The expected label. * @param string $description * The expected node type description. * @param string $help * The expected help text. */ protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $body_label = NULL) { /** @var \Drupal\node\NodeTypeInterface $entity */ $entity = NodeType::load($id); $this->assertTrue($entity instanceof NodeTypeInterface); $this->assertIdentical($label, $entity->label()); $this->assertIdentical($description, $entity->getDescription()); $this->assertIdentical($help, $entity->getHelp()); $this->assertIdentical($display_submitted, $entity->displaySubmitted(), 'Submission info is displayed'); $this->assertIdentical($new_revision, $entity->isNewRevision(), 'Is a new revision'); if ($body_label) { /** @var \Drupal\field\FieldConfigInterface $body */ $body = FieldConfig::load('node.' . $id . '.body'); $this->assertTrue($body instanceof FieldConfigInterface); $this->assertIdentical($body_label, $body->label()); } }
/** * Verifies that content without prior moderation information can be moderated. */ public function testLegacyContent() { $node_type = NodeType::create(['type' => 'example']); $node_type->save(); $node = Node::create(['type' => 'example', 'title' => 'Test title']); $node->save(); // Enable moderation for Articles. /** @var NodeType $node_type */ $node_type = NodeType::load('example'); $node_type->setThirdPartySetting('workbench_moderation', 'enabled', TRUE); $node_type->setThirdPartySetting('workbench_moderation', 'allowed_moderation_states', ['draft', 'needs_review', 'published']); $node_type->setThirdPartySetting('workbench_moderation', 'default_moderation_state', 'draft'); $node_type->save(); // Having no previous state should not break validation. $violations = $node->validate(); $this->assertCount(0, $violations); }
/** * Tests the book_system_info_alter() method. */ public function testBookUninstall() { // No nodes exist. $module_data = _system_rebuild_module_data(); $this->assertFalse(isset($module_data['book']->info['required']), 'The book module is not required.'); $content_type = NodeType::create(array('type' => $this->randomMachineName(), 'name' => $this->randomString())); $content_type->save(); $book_config = $this->config('book.settings'); $allowed_types = $book_config->get('allowed_types'); $allowed_types[] = $content_type->id(); $book_config->set('allowed_types', $allowed_types)->save(); $node = Node::create(array('type' => $content_type->id())); $node->book['bid'] = 'new'; $node->save(); // One node in a book but not of type book. $module_data = _system_rebuild_module_data(); $this->assertTrue($module_data['book']->info['required'], 'The book module is required.'); $this->assertEqual($module_data['book']->info['explanation'], t('To uninstall Book, delete all content that is part of a book.')); $book_node = Node::create(array('type' => 'book')); $book_node->book['bid'] = FALSE; $book_node->save(); // Two nodes, one in a book but not of type book and one book node (which is // not in a book). $module_data = _system_rebuild_module_data(); $this->assertTrue($module_data['book']->info['required'], 'The book module is required.'); $this->assertEqual($module_data['book']->info['explanation'], t('To uninstall Book, delete all content that is part of a book.')); $node->delete(); // One node of type book but not actually part of a book. $module_data = _system_rebuild_module_data(); $this->assertTrue($module_data['book']->info['required'], 'The book module is required.'); $this->assertEqual($module_data['book']->info['explanation'], t('To uninstall Book, delete all content that has the Book content type.')); $book_node->delete(); // No nodes exist therefore the book module is not required. $module_data = _system_rebuild_module_data(); $this->assertFalse(isset($module_data['book']->info['required']), 'The book module is not required.'); $node = Node::create(array('type' => $content_type->id())); $node->save(); // One node exists but is not part of a book therefore the book module is // not required. $module_data = _system_rebuild_module_data(); $this->assertFalse(isset($module_data['book']->info['required']), 'The book module is not required.'); // Uninstall the Book module and check the node type is deleted. \Drupal::service('module_installer')->uninstall(array('book')); $this->assertNull(NodeType::load('book'), "The book node type does not exist."); }
/** * Tests rebuilding the node access permissions table with content. */ public function testNodeAccessRebuildNodeGrants() { \Drupal::service('module_installer')->install(['node_access_test']); \Drupal::state()->set('node_access_test.private', TRUE); node_access_test_add_field(NodeType::load('page')); $this->resetAll(); // Create 30 nodes so that _node_access_rebuild_batch_operation() has to run // more than once. for ($i = 0; $i < 30; $i++) { $nodes[] = $this->drupalCreateNode(array('uid' => $this->webUser->id(), 'private' => [['value' => 1]])); } /** @var \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage */ $grant_storage = \Drupal::service('node.grant_storage'); // Default realm access and node records are present. foreach ($nodes as $node) { $this->assertTrue($node->private->value); $this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the node author.'); $this->assertTrue($grant_storage->access($node, 'view', $this->adminUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the admin user.'); } $this->assertEqual(1, \Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is an all realm access record'); $this->assertTrue(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt'); // Rebuild permissions. $this->drupalGet('admin/reports/status'); $this->clickLink(t('Rebuild permissions')); $this->drupalPostForm(NULL, array(), t('Rebuild permissions')); $this->assertText(t('The content access permissions have been rebuilt.')); // Test if the rebuild by user that cannot bypass node access and does not // have access to the nodes has been successful. $this->assertFalse($this->adminUser->hasPermission('bypass node access')); $this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt'); foreach ($nodes as $node) { $this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.'); $this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.'); } $this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record'); // Test an anonymous node access rebuild from code. $this->drupalLogout(); node_access_rebuild(); foreach ($nodes as $node) { $this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.'); $this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.'); } $this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record'); }
/** * Tests creating a content type during config import. */ public function testImportCreate() { $node_type_id = 'import'; $node_type_config_name = "node.type.{$node_type_id}"; // Simulate config data to import. $active = $this->container->get('config.storage'); $staging = $this->container->get('config.storage.staging'); $this->copyConfig($active, $staging); // Manually add new node type. $src_dir = drupal_get_path('module', 'node_test_config') . '/staging'; $target_dir = $this->configDirectories[CONFIG_STAGING_DIRECTORY]; $this->assertTrue(file_unmanaged_copy("{$src_dir}/{$node_type_config_name}.yml", "{$target_dir}/{$node_type_config_name}.yml")); // Import the content of the staging directory. $this->configImporter()->import(); // Check that the content type was created. $node_type = NodeType::load($node_type_id); $this->assertTrue($node_type, 'Import node type from staging was created.'); $this->assertFalse(FieldConfig::loadByName('node', $node_type_id, 'body')); }
/** * {@inheritdoc} */ public function setUp() { parent::setUp(); // Load the custom node type and check it . $this->custom_type = 'scheduler_api_test'; $this->nodetype = NodeType::load($this->custom_type); if ($this->nodetype) { $this->pass('Custom node type ' . $this->custom_type . ' "' . $this->nodetype->get('name') . '" created during install'); // Do not need to enable this node type for scheduler as that is already // pre-configured in node.type.scheduler_api_test.yml } else { $this->fail('*** Custom node type ' . $this->custom_type . ' does not exist. Testing abandoned ***'); return; } // Create a web user for this content type. $this->webUser = $this->drupalCreateUser(['create ' . $this->custom_type . ' content', 'edit any ' . $this->custom_type . ' content', 'schedule publishing of nodes']); // Create node_storage property. $this->node_storage = $this->container->get('entity.manager')->getStorage('node'); }
/** * Tests that field overrides work as expected. */ public function testFieldOverrides() { if (!NodeType::load('ponies')) { NodeType::create(['name' => 'Ponies', 'type' => 'ponies'])->save(); } $override = BaseFieldOverride::loadByName('node', 'ponies', 'uid'); if ($override) { $override->delete(); } $uid_field = \Drupal::entityManager()->getBaseFieldDefinitions('node')['uid']; $config = $uid_field->getConfig('ponies'); $config->save(); $this->assertEqual($config->get('default_value_callback'), 'Drupal\\node\\Entity\\Node::getCurrentUserId'); /** @var \Drupal\node\NodeInterface $node */ $node = Node::create(['type' => 'ponies']); $owner = $node->getOwner(); $this->assertTrue($owner instanceof UserInterface); $this->assertEqual($owner->id(), $this->user->id()); }
/** * Tests creating a content type during config import. */ public function testImportCreate() { $node_type_id = 'import'; $node_type_config_name = "node.type.{$node_type_id}"; // Simulate config data to import. $active = $this->container->get('config.storage'); $sync = $this->container->get('config.storage.sync'); $this->copyConfig($active, $sync); // Manually add new node type. $src_dir = __DIR__ . '/../../../modules/node_test_config/sync'; $target_dir = config_get_config_directory(CONFIG_SYNC_DIRECTORY); $this->assertTrue(file_unmanaged_copy("{$src_dir}/{$node_type_config_name}.yml", "{$target_dir}/{$node_type_config_name}.yml")); // Import the content of the sync directory. $this->configImporter()->import(); // Check that the content type was created. $node_type = NodeType::load($node_type_id); $this->assertTrue($node_type, 'Import node type from sync was created.'); $this->assertFalse(FieldConfig::loadByName('node', $node_type_id, 'body')); }
/** * Tests importing an updated content type. */ public function testImportChange() { $node_type_id = 'default'; $node_type_config_name = "node.type.{$node_type_id}"; // Simulate config data to import: // - a modified version (modified label) of the node type config. $active = $this->container->get('config.storage'); $sync = $this->container->get('config.storage.sync'); $this->copyConfig($active, $sync); $node_type = $active->read($node_type_config_name); $new_label = 'Test update import field'; $node_type['name'] = $new_label; // Save as files in the sync directory. $sync->write($node_type_config_name, $node_type); // Import the content of the sync directory. $this->configImporter()->import(); // Check that the updated config was correctly imported. $node_type = NodeType::load($node_type_id); $this->assertEqual($node_type->label(), $new_label, 'Node type name has been updated.'); }
/** * Creates a custom content type based on default settings. * * @param array $values * An array of settings to change from the defaults. * Example: 'type' => 'foo'. * * @return \Drupal\node\Entity\NodeType * Created content type. */ protected function createContentType(array $values = array()) { // Find a non-existent random type name. if (!isset($values['type'])) { do { $id = strtolower($this->randomMachineName(8)); } while (NodeType::load($id)); } else { $id = $values['type']; } $values += array('type' => $id, 'name' => $id); $type = NodeType::create($values); $status = $type->save(); node_add_body_field($type); if ($this instanceof \PHPUnit_Framework_TestCase) { $this->assertSame($status, SAVED_NEW, (new FormattableMarkup('Created content type %type.', array('%type' => $type->id())))->__toString()); } else { $this->assertEqual($status, SAVED_NEW, (new FormattableMarkup('Created content type %type.', array('%type' => $type->id())))->__toString()); } return $type; }
/** * Tests date input is displayed as vertical tab or an expandable fieldset. */ public function testFieldsDisplay() { /** @var NodeTypeInterface $node_type */ $node_type = NodeType::load('page'); $this->drupalLogin($this->adminUser); // Check that the dates are shown in a vertical tab by default. $this->drupalGet('node/add/page'); $this->assertTrue($this->xpath('//div[contains(@class, "form-type-vertical-tabs")]//details[@id = "edit-scheduler-settings"]'), 'By default the scheduler dates are shown in a vertical tab.'); // Check that the dates are shown as a fieldset when configured to do so. $node_type->setThirdPartySetting('scheduler', 'fields_display_mode', 'fieldset')->save(); $this->drupalGet('node/add/page'); $this->assertFalse($this->xpath('//div[contains(@class, "form-type-vertical-tabs")]//details[@id = "edit-scheduler-settings"]'), 'The scheduler dates are not shown in a vertical tab when they are configured to show as a fieldset.'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings"]'), 'The scheduler dates are shown in a fieldset when they are configured to show as a fieldset.'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and not(@open = "open")]'), 'The scheduler dates fieldset is collapsed by default.'); // Check that the fieldset is expanded if either of the scheduling dates // are required. $node_type->setThirdPartySetting('scheduler', 'publish_required', TRUE)->save(); $this->drupalGet('node/add/page'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and @open = "open"]'), 'The scheduler dates are shown in an expanded fieldset when the publish-on date is required.'); $node_type->setThirdPartySetting('scheduler', 'publish_required', FALSE)->setThirdPartySetting('scheduler', 'unpublish_required', TRUE)->save(); $this->drupalGet('node/add/page'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and @open = "open"]'), 'The scheduler dates are shown in an expanded fieldset when the unpublish-on date is required.'); // Check that the fieldset is expanded if the 'always' option is set. $node_type->setThirdPartySetting('scheduler', 'publish_required', FALSE)->setThirdPartySetting('scheduler', 'unpublish_required', FALSE)->setThirdPartySetting('scheduler', 'expand_fieldset', 'always')->save(); $this->drupalGet('node/add/page'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and @open = "open"]'), 'The scheduler dates are shown in an expanded fieldset when the option to always expand is turned on.'); // Check that the fieldset is expanded if the node already has a publish-on // date. This requires editing an existing scheduled node. $node_type->setThirdPartySetting('scheduler', 'expand_fieldset', 'when_required')->save(); $options = ['title' => 'Contains Publish-on date ' . $this->randomMachineName(10), 'type' => 'page', 'publish_on' => strtotime('+1 day')]; $node = $this->drupalCreateNode($options); $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and @open = "open"]'), 'The scheduler dates are shown in an expanded fieldset when a publish-on date already exists.'); // Check that the fieldset is expanded if the node has an unpublish-on date. $options = ['title' => 'Contains Unpublish-on date ' . $this->randomMachineName(10), 'type' => 'page', 'unpublish_on' => strtotime('+1 day')]; $node = $this->drupalCreateNode($options); $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertTrue($this->xpath('//details[@id = "edit-scheduler-settings" and @open = "open"]'), 'The scheduler dates are shown in an expanded fieldset when an unpublish-on date already exists.'); }
/** * Checks whether emails get sent when a user creates a new article node. */ function testMandrillEmail() { $this->installEntitySchema('node'); $this->installEntitySchema('user'); \Drupal::service('module_installer')->install(array('dblog', 'field')); // Use the state system collector mail backend. $config = \Drupal::configFactory()->getEditable('system.mail'); $mail_plugins = array('default' => 'test_mail_collector'); $config->set('interface', $mail_plugins)->save(); // Reset the state variable that holds sent messages. \Drupal::state()->set('system.test_mail_collector', array()); // Create a user account and set it as the current user. $user = entity_create('user', array('uid' => 1, 'mail' => '*****@*****.**')); $user->save(); \Drupal::currentUser()->setAccount($user); // Set the site email address. \Drupal::configFactory()->getEditable('system.site')->set('mail', '*****@*****.**')->save(); // Create the article content type and add the body field to it. $type = array('type' => 'article', 'name' => 'Article'); $type = entity_create('node_type', $type); $type->save(); $article = NodeType::load('article'); node_add_body_field($article); // Create a random article node. $title = $this->randomMachineName(); $values = array('uid' => $user->id(), 'title' => $title, 'body' => [['value' => 'test_body']], 'type' => 'article'); $node = entity_create('node', $values); $node->save(); // Check the latest captured emails. $captured_emails = \Drupal::state()->get('system.test_mail_collector'); $sent_message = end($captured_emails); $this->assertTrue(!empty($sent_message)); $this->assertEqual($sent_message['id'], 'd8mail_node_insert', 'Correct mail id.'); $this->assertEqual($sent_message['to'], '*****@*****.**', 'Correct mail to.'); $this->assertEqual($sent_message['from'], '*****@*****.**', 'Correct mail from.'); $this->assertEqual($sent_message['subject'], sprintf("Node created: %s", $title), 'Correct mail subject.'); $this->assertEqual($sent_message['body'], 'test_body' . PHP_EOL, 'Correct mail body.'); }
/** * Tests breadcrumbs on node and administrative paths. */ function testBreadCrumbs() { // Prepare common base breadcrumb elements. $home = array('' => 'Home'); $admin = $home + array('admin' => t('Administration')); $config = $admin + array('admin/config' => t('Configuration')); $type = 'article'; // Verify Taxonomy administration breadcrumbs. $trail = $admin + array('admin/structure' => t('Structure')); $this->assertBreadcrumb('admin/structure/taxonomy', $trail); $trail += array('admin/structure/taxonomy' => t('Taxonomy')); $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags', $trail); $trail += array('admin/structure/taxonomy/manage/tags' => t('Tags')); $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags/overview', $trail); $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags/add', $trail); // Verify Menu administration breadcrumbs. $trail = $admin + array('admin/structure' => t('Structure')); $this->assertBreadcrumb('admin/structure/menu', $trail); $trail += array('admin/structure/menu' => t('Menus')); $this->assertBreadcrumb('admin/structure/menu/manage/tools', $trail); $trail += array('admin/structure/menu/manage/tools' => t('Tools')); $this->assertBreadcrumb("admin/structure/menu/link/node.add_page/edit", $trail); $this->assertBreadcrumb('admin/structure/menu/manage/tools/add', $trail); // Verify Node administration breadcrumbs. $trail = $admin + array('admin/structure' => t('Structure'), 'admin/structure/types' => t('Content types')); $this->assertBreadcrumb('admin/structure/types/add', $trail); $this->assertBreadcrumb("admin/structure/types/manage/{$type}", $trail); $trail += array("admin/structure/types/manage/{$type}" => t('Article')); $this->assertBreadcrumb("admin/structure/types/manage/{$type}/fields", $trail); $this->assertBreadcrumb("admin/structure/types/manage/{$type}/display", $trail); $trail_teaser = $trail + array("admin/structure/types/manage/{$type}/display" => t('Manage display')); $this->assertBreadcrumb("admin/structure/types/manage/{$type}/display/teaser", $trail_teaser); $this->assertBreadcrumb("admin/structure/types/manage/{$type}/delete", $trail); $trail += array("admin/structure/types/manage/{$type}/fields" => t('Manage fields')); $this->assertBreadcrumb("admin/structure/types/manage/{$type}/fields/node.{$type}.body", $trail); // Verify Filter text format administration breadcrumbs. $filter_formats = filter_formats(); $format = reset($filter_formats); $format_id = $format->id(); $trail = $config + array('admin/config/content' => t('Content authoring')); $this->assertBreadcrumb('admin/config/content/formats', $trail); $trail += array('admin/config/content/formats' => t('Text formats and editors')); $this->assertBreadcrumb('admin/config/content/formats/add', $trail); $this->assertBreadcrumb("admin/config/content/formats/manage/{$format_id}", $trail); // @todo Remove this part once we have a _title_callback, see // https://www.drupal.org/node/2076085. $trail += array("admin/config/content/formats/manage/{$format_id}" => $format->label()); $this->assertBreadcrumb("admin/config/content/formats/manage/{$format_id}/disable", $trail); // Verify node breadcrumbs (without menu link). $node1 = $this->drupalCreateNode(); $nid1 = $node1->id(); $trail = $home; $this->assertBreadcrumb("node/{$nid1}", $trail); // Also verify that the node does not appear elsewhere (e.g., menu trees). $this->assertNoLink($node1->getTitle()); // Also verify that the node does not appear elsewhere (e.g., menu trees). $this->assertNoLink($node1->getTitle()); $trail += array("node/{$nid1}" => $node1->getTitle()); $this->assertBreadcrumb("node/{$nid1}/edit", $trail); // Verify that breadcrumb on node listing page contains "Home" only. $trail = array(); $this->assertBreadcrumb('node', $trail); // Verify node breadcrumbs (in menu). // Do this separately for Main menu and Tools menu, since only the // latter is a preferred menu by default. // @todo Also test all themes? Manually testing led to the suspicion that // breadcrumbs may differ, possibly due to theme overrides. $menus = array('main', 'tools'); // Alter node type menu settings. $node_type = NodeType::load($type); $node_type->setThirdPartySetting('menu_ui', 'available_menus', $menus); $node_type->setThirdPartySetting('menu_ui', 'parent', 'tools:'); $node_type->save(); foreach ($menus as $menu) { // Create a parent node in the current menu. $title = $this->randomMachineName(); $node2 = $this->drupalCreateNode(array('type' => $type, 'title' => $title, 'menu' => array('enabled' => 1, 'title' => 'Parent ' . $title, 'description' => '', 'menu_name' => $menu, 'parent' => ''))); if ($menu == 'tools') { $parent = $node2; } } // Create a Tools menu link for 'node', move the last parent node menu // link below it, and verify a full breadcrumb for the last child node. $menu = 'tools'; $edit = array('title[0][value]' => 'Root', 'link[0][uri]' => '/node'); $this->drupalPostForm("admin/structure/menu/manage/{$menu}/add", $edit, t('Save')); $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => 'Root')); $link = reset($menu_links); $edit = array('menu[menu_parent]' => $link->getMenuName() . ':' . $link->getPluginId()); $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save and keep published')); $expected = array("node" => $link->getTitle()); $trail = $home + $expected; $tree = $expected + array('node/' . $parent->id() => $parent->menu['title']); $trail += array('node/' . $parent->id() => $parent->menu['title']); // Add a taxonomy term/tag to last node, and add a link for that term to the // Tools menu. $tags = array('Drupal' => array(), 'Breadcrumbs' => array()); $edit = array('field_tags[target_id]' => implode(',', array_keys($tags))); $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save and keep published')); // Put both terms into a hierarchy Drupal » Breadcrumbs. Required for both // the menu links and the terms itself, since taxonomy_term_page() resets // the breadcrumb based on taxonomy term hierarchy. $parent_tid = 0; foreach ($tags as $name => $null) { $terms = entity_load_multiple_by_properties('taxonomy_term', array('name' => $name)); $term = reset($terms); $tags[$name]['term'] = $term; if ($parent_tid) { $edit = array('parent[]' => array($parent_tid)); $this->drupalPostForm("taxonomy/term/{$term->id()}/edit", $edit, t('Save')); } $parent_tid = $term->id(); } $parent_mlid = ''; foreach ($tags as $name => $data) { $term = $data['term']; $edit = array('title[0][value]' => "{$name} link", 'link[0][uri]' => "/taxonomy/term/{$term->id()}", 'menu_parent' => "{$menu}:{$parent_mlid}", 'enabled[value]' => 1); $this->drupalPostForm("admin/structure/menu/manage/{$menu}/add", $edit, t('Save')); $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $edit['title[0][value]'], 'link.uri' => 'internal:/taxonomy/term/' . $term->id())); $tags[$name]['link'] = reset($menu_links); $parent_mlid = $tags[$name]['link']->getPluginId(); } // Verify expected breadcrumbs for menu links. $trail = $home; $tree = array(); // Logout the user because we want to check the active class as well, which // is just rendered as anonymous user. $this->drupalLogout(); foreach ($tags as $name => $data) { $term = $data['term']; /** @var \Drupal\menu_link_content\MenuLinkContentInterface $link */ $link = $data['link']; $link_path = $link->getUrlObject()->getInternalPath(); $tree += array($link_path => $link->getTitle()); $this->assertBreadcrumb($link_path, $trail, $term->getName(), $tree); $this->assertEscaped($parent->getTitle(), 'Tagged node found.'); // Additionally make sure that this link appears only once; i.e., the // untranslated menu links automatically generated from menu router items // ('taxonomy/term/%') should never be translated and appear in any menu // other than the breadcrumb trail. $elements = $this->xpath('//nav[@id=:menu]/descendant::a[@href=:href]', array(':menu' => 'block-bartik-tools', ':href' => Url::fromUri('base:' . $link_path)->toString())); $this->assertTrue(count($elements) == 1, "Link to {$link_path} appears only once."); // Next iteration should expect this tag as parent link. // Note: Term name, not link name, due to taxonomy_term_page(). $trail += array($link_path => $term->getName()); } // Verify breadcrumbs on user and user/%. // We need to log back in and out below, and cannot simply grant the // 'administer users' permission, since user_page() makes your head explode. user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, array('access user profiles')); // Verify breadcrumb on front page. $this->assertBreadcrumb('<front>', array()); // Verify breadcrumb on user pages (without menu link) for anonymous user. $trail = $home; $this->assertBreadcrumb('user', $trail, t('Log in')); $this->assertBreadcrumb('user/' . $this->adminUser->id(), $trail, $this->adminUser->getUsername()); // Verify breadcrumb on user pages (without menu link) for registered users. $this->drupalLogin($this->adminUser); $trail = $home; $this->assertBreadcrumb('user', $trail, $this->adminUser->getUsername()); $this->assertBreadcrumb('user/' . $this->adminUser->id(), $trail, $this->adminUser->getUsername()); $trail += array('user/' . $this->adminUser->id() => $this->adminUser->getUsername()); $this->assertBreadcrumb('user/' . $this->adminUser->id() . '/edit', $trail, $this->adminUser->getUsername()); // Create a second user to verify breadcrumb on user pages again. $this->webUser = $this->drupalCreateUser(array('administer users', 'access user profiles')); $this->drupalLogin($this->webUser); // Verify correct breadcrumb and page title on another user's account pages. $trail = $home; $this->assertBreadcrumb('user/' . $this->adminUser->id(), $trail, $this->adminUser->getUsername()); $trail += array('user/' . $this->adminUser->id() => $this->adminUser->getUsername()); $this->assertBreadcrumb('user/' . $this->adminUser->id() . '/edit', $trail, $this->adminUser->getUsername()); // Verify correct breadcrumb and page title when viewing own user account. $trail = $home; $this->assertBreadcrumb('user/' . $this->webUser->id(), $trail, $this->webUser->getUsername()); $trail += array('user/' . $this->webUser->id() => $this->webUser->getUsername()); $this->assertBreadcrumb('user/' . $this->webUser->id() . '/edit', $trail, $this->webUser->getUsername()); // Create an only slightly privileged user being able to access site reports // but not administration pages. $this->webUser = $this->drupalCreateUser(array('access site reports')); $this->drupalLogin($this->webUser); // Verify that we can access recent log entries, there is a corresponding // page title, and that the breadcrumb is just the Home link (because the // user is not able to access "Administer". $trail = $home; $this->assertBreadcrumb('admin', $trail, t('Access denied')); $this->assertResponse(403); // Since the 'admin' path is not accessible, we still expect only the Home // link. $this->assertBreadcrumb('admin/reports', $trail, t('Reports')); $this->assertNoResponse(403); // Since the Reports page is accessible, that will show. $trail += array('admin/reports' => t('Reports')); $this->assertBreadcrumb('admin/reports/dblog', $trail, t('Recent log messages')); $this->assertNoResponse(403); // Ensure that the breadcrumb is safe against XSS. $this->drupalGet('menu-test/breadcrumb1/breadcrumb2/breadcrumb3'); $this->assertRaw('<script>alert(12);</script>'); $this->assertEscaped('<script>alert(123);</script>'); }
/** * Creates a content type. * * Attempts to create a content type with ID $prefix, $prefix_1, $prefix_2... * * @param string $prefix * A string prefix for the node type ID. * * @return \Drupal\node\NodeTypeInterface * A node type entity. */ private function createContentType($prefix) { // Generate a unique ID. $i = 0; $separator = '_'; $id = $prefix; while (NodeType::load($id)) { $i++; $id = $prefix . $separator . $i; } $node_type = NodeType::create(['type' => $id, 'name' => $id]); $node_type->save(); return $node_type; }
/** * Tests the loading of Quick Edit when a user does have access to it. * * Also ensures lazy loading of in-place editors works. */ public function testUserWithPermission() { $this->drupalLogin($this->editorUser); $this->drupalGet('node/1'); // Library and in-place editors. $settings = $this->getDrupalSettings(); $libraries = explode(',', $settings['ajaxPageState']['libraries']); $this->assertTrue(in_array('quickedit/quickedit', $libraries), 'Quick Edit library loaded.'); $this->assertFalse(in_array('quickedit/quickedit.inPlaceEditor.form', $libraries), "'form' in-place editor not loaded."); // HTML annotation must always exist (to not break the render cache). $this->assertRaw('data-quickedit-entity-id="node/1"'); $this->assertRaw('data-quickedit-field-id="node/1/body/en/full"'); // There should be only one revision so far. $node = Node::load(1); $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); $this->assertIdentical(1, count($vids), 'The node has only one revision.'); $original_log = $node->revision_log->value; // Retrieving the metadata should result in a 200 JSON response. $htmlPageDrupalSettings = $this->drupalSettings; $post = array('fields[0]' => 'node/1/body/en/full'); $response = $this->drupalPostWithFormat('quickedit/metadata', 'json', $post); $this->assertResponse(200); $expected = array('node/1/body/en/full' => array('label' => 'Body', 'access' => TRUE, 'editor' => 'form')); $this->assertIdentical(Json::decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.'); // Restore drupalSettings to build the next requests; simpletest wipes them // after a JSON response. $this->drupalSettings = $htmlPageDrupalSettings; // Retrieving the attachments should result in a 200 response, containing: // 1. a settings command with useless metadata: AjaxController is dumb // 2. an insert command that loads the required in-place editors $post = array('editors[0]' => 'form') + $this->getAjaxPageStatePostData(); $response = $this->drupalPost('quickedit/attachments', 'application/vnd.drupal-ajax', $post); $ajax_commands = Json::decode($response); $this->assertIdentical(2, count($ajax_commands), 'The attachments HTTP request results in two AJAX commands.'); // First command: settings. $this->assertIdentical('settings', $ajax_commands[0]['command'], 'The first AJAX command is a settings command.'); // Second command: insert libraries into DOM. $this->assertIdentical('insert', $ajax_commands[1]['command'], 'The second AJAX command is an append command.'); $this->assertTrue(in_array('quickedit/quickedit.inPlaceEditor.form', explode(',', $ajax_commands[0]['settings']['ajaxPageState']['libraries'])), 'The quickedit.inPlaceEditor.form library is loaded.'); // Retrieving the form for this field should result in a 200 response, // containing only a quickeditFieldForm command. $post = array('nocssjs' => 'true', 'reset' => 'true') + $this->getAjaxPageStatePostData(); $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', 'application/vnd.drupal-ajax', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); // Prepare form values for submission. drupalPostAjaxForm() is not suitable // for handling pages with JSON responses, so we need our own solution here. $form_tokens_found = preg_match('/\\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); if ($form_tokens_found) { $edit = array('body[0][summary]' => '', 'body[0][value]' => '<p>Fine thanks.</p>', 'body[0][format]' => 'filtered_html', 'op' => t('Save')); $post = array('form_id' => 'quickedit_field_form', 'form_token' => $token_match[1], 'form_build_id' => $build_id_match[1]); $post += $edit + $this->getAjaxPageStatePostData(); // Submit field form and check response. This should store the updated // entity in PrivateTempStore on the server. $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', 'application/vnd.drupal-ajax', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); $this->assertIdentical($ajax_commands[0]['other_view_modes'], array(), 'Field was not rendered in any other view mode.'); // Ensure the text on the original node did not change yet. $this->drupalGet('node/1'); $this->assertText('How are you?'); // Save the entity by moving the PrivateTempStore values to entity storage. $post = array('nocssjs' => 'true'); $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands), 'The entity submission HTTP request results in one AJAX command.'); $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditEntitySaved command.'); $this->assertIdentical($ajax_commands[0]['data']['entity_type'], 'node', 'Saved entity is of type node.'); $this->assertIdentical($ajax_commands[0]['data']['entity_id'], '1', 'Entity id is 1.'); // Ensure the text on the original node did change. $this->drupalGet('node/1'); $this->assertText('Fine thanks.'); // Ensure no new revision was created and the log message is unchanged. $node = Node::load(1); $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); $this->assertIdentical(1, count($vids), 'The node has only one revision.'); $this->assertIdentical($original_log, $node->revision_log->value, 'The revision log message is unchanged.'); // Now configure this node type to create new revisions automatically, // then again retrieve the field form, fill it, submit it (so it ends up // in PrivateTempStore) and then save the entity. Now there should be two // revisions. $node_type = NodeType::load('article'); $node_type->setNewRevision(TRUE); $node_type->save(); // Retrieve field form. $post = array('nocssjs' => 'true', 'reset' => 'true'); $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', 'application/vnd.drupal-ajax', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); // Submit field form. preg_match('/\\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match); preg_match('/\\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); $edit['body[0][value]'] = '<p>kthxbye</p>'; $post = array('form_id' => 'quickedit_field_form', 'form_token' => $token_match[1], 'form_build_id' => $build_id_match[1]); $post += $edit + $this->getAjaxPageStatePostData(); $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', 'application/vnd.drupal-ajax', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditFieldFormSaved command.'); $this->assertTrue(strpos($ajax_commands[0]['data'], 'kthxbye'), 'Form value saved and printed back.'); // Save the entity. $post = array('nocssjs' => 'true'); $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); $this->assertResponse(200); $ajax_commands = Json::decode($response); $this->assertIdentical(1, count($ajax_commands)); $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditEntitySaved command.'); $this->assertEqual($ajax_commands[0]['data'], ['entity_type' => 'node', 'entity_id' => 1], 'Updated entity type and ID returned'); // Test that a revision was created with the correct log message. $vids = \Drupal::entityManager()->getStorage('node')->revisionIds(Node::load(1)); $this->assertIdentical(2, count($vids), 'The node has two revisions.'); $revision = node_revision_load($vids[0]); $this->assertIdentical($original_log, $revision->revision_log->value, 'The first revision log message is unchanged.'); $revision = node_revision_load($vids[1]); $this->assertIdentical('Updated the <em class="placeholder">Body</em> field through in-place editing.', $revision->revision_log->value, 'The second revision log message was correctly generated by Quick Edit module.'); } }
/** * Creates a custom content type based on default settings. * * @param array $values * An array of settings to change from the defaults. * Example: 'type' => 'foo'. * * @return \Drupal\node\Entity\NodeType * Created content type. */ protected function drupalCreateContentType(array $values = array()) { // Find a non-existent random type name. if (!isset($values['type'])) { do { $id = strtolower($this->randomMachineName(8)); } while (NodeType::load($id)); } else { $id = $values['type']; } $values += array('type' => $id, 'name' => $id); $type = entity_create('node_type', $values); $status = $type->save(); node_add_body_field($type); \Drupal::service('router.builder')->rebuild(); $this->assertEqual($status, SAVED_NEW, SafeMarkup::format('Created content type %type.', array('%type' => $type->id()))); return $type; }
/** * Checks the node preview functionality, when using revisions. */ function testPagePreviewWithRevisions() { $title_key = 'title[0][value]'; $body_key = 'body[0][value]'; $term_key = $this->fieldName . '[target_id]'; // Force revision on "Basic page" content. $node_type = NodeType::load('page'); $node_type->setNewRevision(TRUE); $node_type->save(); // Fill in node creation form and preview node. $edit = array(); $edit[$title_key] = $this->randomMachineName(8); $edit[$body_key] = $this->randomMachineName(16); $edit[$term_key] = $this->term->id(); $edit['revision_log[0][value]'] = $this->randomString(32); $this->drupalPostForm('node/add/page', $edit, t('Preview')); // Check that the preview is displaying the title, body and term. $this->assertTitle(t('@title | Drupal', array('@title' => $edit[$title_key])), 'Basic page title is preview.'); $this->assertText($edit[$title_key], 'Title displayed.'); $this->assertText($edit[$body_key], 'Body displayed.'); $this->assertText($edit[$term_key], 'Term displayed.'); // Check that the title and body fields are displayed with the correct // values after going back to the content edit page. $this->clickLink(t('Back to content editing')); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); // Check that the revision log field has the correct value. $this->assertFieldByName('revision_log[0][value]', $edit['revision_log[0][value]'], 'Revision log field displayed.'); }