drupal 8 programmatically create node can only succeeded once - php

I'v created a new module in Drupal 8, it is just a hello world example.
the code just like the following
class FirstController{
public function content(){
return array(
'#type' => 'markup',
'#markup' => t('G\'day.............'),
);
// <------I added the new node code here
}
}
and I added the following code to the content() function to create a node .
but I found that it can only create the node once, and after that no matter how many time I refresh the module page it won't be creating any more new node again.
use \Drupal\node\Entity\Node;
use \Drupal\file\Entity\File;
// Create file object from remote URL.
$data = file_get_contents('https://www.drupal.org/files/druplicon.small_.png');
$file = file_save_data($data, 'public://druplicon.png', FILE_EXISTS_REPLACE);
// Create node object with attached file.
$node = Node::create([
'type' => 'article',
'title' => 'Druplicon test',
'field_image' => [
'target_id' => $file->id(),
'alt' => 'Hello world',
'title' => 'Goodbye world'
],
]);
$node->save();
any thing I'm doing wrong here?

You forgot about caching :) Your output is just cached, and that's why your code is called only once (to be more precise, not once, but until cache is valid). Take a look here: Render API and here: Cacheability of render arrays.
To disable caching for current page request you may use the following code:
\Drupal::service('page_cache_kill_switch')->trigger();
So, your controller method may look like the following:
public function content() {
// Create file object from remote URL.
$data = file_get_contents('https://www.drupal.org/files/druplicon.small_.png');
/** #var FileInterface $file */
$file = file_save_data($data, 'public://druplicon.png', FILE_EXISTS_RENAME);
// Create node object with attached file.
$node = Node::create([
'type' => 'article',
'title' => 'Druplicon test',
'field_image' => [
'target_id' => $file->id(),
'alt' => 'Hello world',
'title' => 'Goodbye world'
],
]);
$node->save();
\Drupal::service('page_cache_kill_switch')->trigger();
return array(
'#markup' => 'Something ' . rand(),
);
}

Related

How to extend Parsedown to add a class to table tags

I want to write an extension for Parsedown so that I can add a default class to each of the table tags. I've found that I can successfully hack the source code by adding lines to assign attributes in the blockTable function (around line 870):
$Block = array(
'alignments' => $alignments,
'identified' => true,
'element' => array(
'name' => 'table',
'handler' => 'elements',
'attributes' => array(
'class' => 'table',
),
),
);
However, if I try to loosely follow the Change Element Markup extension tutorial I am unsuccessful (perhaps because the table parsing may be an iterative process and the example in the tutorial is a simple string replacement.)
I've tried:
class Extension extends Parsedown
{
protected function blockTable($Line, array $Block = null)
{
$Block = parent::blockTable($Line, array $Block = null);
$Block['table']['attributes']['class'] = 'table';
return $Block;
}
}
but that doesn't work.
I'm not too sure what's wrong with your code, as your code matches mine. I simply added
'attributes' => array(
'class' => 'table table-responsive'
),
to identifyTable, on line 850 so that it became
$Block = array(
'alignments' => $alignments,
'identified' => true,
'element' => array(
'name' => 'table',
'handler' => 'elements',
'attributes' => array(
'class' => 'table table-responsive',
),
),
);
which works just fine for me. But that appears to be the same for you, minus table-responsive.
What version are you using?
I know this is a very old question asked 5 years, 3 month ago, but I came across this answer on a google search, so thought it was worthwhile answering with the code which outputs table class.
class Extension extends Parsedown {
protected function blockTable($Line, ?array $Block = null)
{
$Block = parent::blockTable($Line, $Block);
if(is_null($Block)){ return; }
$Block['element']['attributes']['class'] = 'table table-responsive';
return $Block;
}
I encountered exactly the same problem with the symfony demo application.
Finally it turned out that it wasn't parsedown, because the output was cleaned up by the html-sanitizer.
Allowing the class attribute for tables solved the issue.
For symfony 4 demo app add to config/packages/html_sanitizer.yaml:
html_sanitizer:
#[...]
sanitizers:
default:
# [...]
tags:
table:
allowed_attributes:
- "class"

Changing display logic for a field in sugarcrm

I have the following situation: Contacts without a first or last name, in fact, they only have a email address.
I can work with these contacts fine, but when I use the listview anywhere (for instance to show all contacts from a company) there now is no way to click through to the contact (normally you would click on the name).
I'm looking for a way to solve this, for instance by showing a clickable text like 'name not known', but can't figure out how to do this. I've been looking at the manual and in the files in the modules directory and the sugarfields dir, but can't quite figure it out.
The closest I got was in /sugarcrm/modules/Contacts/metadata/listviewdefs.php
where this piece of code resides:
$listViewDefs['Contacts'] = array(
'NAME' => array(
'width' => '20%',
'label' => 'LBL_LIST_NAME',
'link' => true,
'contextMenu' => array('objectType' => 'sugarPerson',
'metaData' => array('contact_id' => '{$ID}',
'module' => 'Contacts',
'return_action' => 'ListView',
'contact_name' => '{$FULL_NAME}',
'parent_id' => '{$ACCOUNT_ID}',
'parent_name' => '{$ACCOUNT_NAME}',
'return_module' => 'Contacts',
'return_action' => 'ListView',
'parent_type' => 'Account',
'notes_parent_type' => 'Account')
),
'orderBy' => 'name',
'default' => true,
'related_fields' => array('first_name', 'last_name', 'salutation', 'account_name', 'account_id'),
),
Somewhere there has to be a function that joins the first and lastname together...
Edit: I found a solution:
The actual concatenation function is in /sugarcrm/include/SugarObjects/templates/person/person.php and is called _create_proper_name_field()
I can modify the output for my specific case by adding something like this to the end of the function:
if (empty(trim($full_name))){
$full_name = 'Name unknown';
}
However, I would rather have a upgrade safe solution, so that will be the next challenge.
Don't edit the core because the next upgrade will break your SugarCRM instance. Use logic hooks to be upgrade safe:
create a file 'logic_hooks.php' in /custom/modules/Contacts/
In that file, add the followin code:
<?php
$hook_array['before_save'][] = Array(1,'logic_fill_name','custom/modules/Contacts/logic_hooks/logics.php','ContactLogics','logic_fill_name');
After you have done this. create the file 'logics.php' in /custom/modules/Contacts/logic_hooks.
In the logics.php file, add something like:
<?php
require_once 'include/SugarQuery/SugarQuery.php';
/**
* Class ContactLogics
*/
class ContactLogics {
/**
* #param $bean
* #param $event
* #param $arguments
*/
public function logic_fill_name($bean, $event, $arguments) {
if (empty(trim($bean->first_name)) && empty(trim($bean->last_name))){
$bean->last_name = 'Name unknown';
}
}
}
Now some explanation. When you edited a recordview and pressed the save button, the logic hook 'before_save' will be triggered. This code will change the full name to 'Name unknown' when the full name is empty. When the 'before_save' is executed, the actual save will take place.

How do I programmatically create a node?

I'm designing a new module in Drupal 8. It's a long-term project that won't be going public for a few months at least, so I'm using it as a way to figure out what's new.
In this module, I want to be able to programmatically create nodes. In Drupal 7, I would do this by creating the object, then calling "node_submit" and "node_save".
These functions no longer exist in Drupal 8. Instead, according to the documentation, "Modules and scripts may programmatically submit nodes using the usual form API pattern." I'm at a loss. What does this mean? I've used Form API to create forms in Drupal 7, but I don't get what the docs are saying here.
What I'm looking to do is programmatically create at least one and possibly multiple new nodes, based on information not taken directly from a user-presented form. I need to be able to:
1) Specify the content type
2) Specify the URL path
3) Set any other necessary variables that would previously have been handled by the now-obsolete node_object_prepare()
4) Commit the new node object
I would prefer to be able to do this in an independent, highly abstracted function not tied to a specific block or form.
So what am I missing?
use Drupal\node\Entity\Node;
$node = Node::create(array(
'type' => 'your_content_type',
'title' => 'your title',
'langcode' => 'en',
'uid' => '1',
'status' => 1,
'field_fields' => array(),
));
$node->save();
RE: deprecated entity create
Here is a short example of usage without the deprecated functions. This is particularly helpful for dynamic creation:
//define entity type and bundle
$entity_type="node";
$bundle="article";
//get definition of target entity type
$entity_def = \Drupal::entityManager()->getDefinition($entity_type);
//load up an array for creation
$new_node=array(
//set title
'title' => 'test node',
//set body
'body' => 'this is a test body, can also be set as an array with "value" and "format" as keys I believe',
//use the entity definition to set the appropriate property for the bundle
$entity_def->get('entity_keys')['bundle']=>$bundle
);
$new_post = \Drupal::entityManager()->getStorage($entity_type)->create($new_node);
$new_post->save();
The Drupal 8 version of devel/devel_generate module has a good example of this.
$edit_node = array(
'nid' => NULL,
'type' => $node_type,
'uid' => $users[array_rand($users)],
'revision' => mt_rand(0, 1),
'status' => TRUE,
'promote' => mt_rand(0, 1),
'created' => REQUEST_TIME - mt_rand(0, $results['time_range']),
'langcode' => devel_generate_get_langcode($results),
);
if ($type->has_title) {
// We should not use the random function if the value is not random
if ($results['title_length'] < 2) {
$edit_node['title'] = devel_create_greeking(1, TRUE);
}
else {
$edit_node['title'] = devel_create_greeking(mt_rand(1, $results['title_length']), TRUE);
}
}
else {
$edit_node['title'] = '';
}
// #todo Remove once comment become field. http://drupal.org/node/731724
if (Drupal::moduleHandler()->moduleExists('comment')) {
$edit_node['comment'] = variable_get('comment_' . $node_type, COMMENT_NODE_OPEN);
}
$node = entity_create('node', $edit_node);
Using formatted text
Using grep with before/after code lines helped me figure out how to add a node with 'full_html'.
Search the Drupal core code with this :
$ cd drupal/core
$ grep -B 5 -A 5 -r entity_create.*node * > /tmp/temp-grep.txt
Then, open up /tmp/temp-grep.txt in a text editor. Poke around there a bit and you'll see this :
--
modules/editor/src/Tests/EditorFileUsageTest.php- $body_value .= '<img src="awesome-llama.jpg" data-editor-file-uuid="invalid-editor-file-uuid-value" />';
modules/editor/src/Tests/EditorFileUsageTest.php- // Test handling of a non-existing UUID.
modules/editor/src/Tests/EditorFileUsageTest.php- $body_value .= '<img src="awesome-llama.jpg" data-editor-file-uuid="30aac704-ba2c-40fc-b609-9ed121aa90f4" />';
modules/editor/src/Tests/EditorFileUsageTest.php- // Test editor_entity_insert(): increment.
modules/editor/src/Tests/EditorFileUsageTest.php- $this->createUser();
modules/editor/src/Tests/EditorFileUsageTest.php: $node = entity_create('node', array(
modules/editor/src/Tests/EditorFileUsageTest.php- 'type' => 'page',
modules/editor/src/Tests/EditorFileUsageTest.php- 'title' => 'test',
modules/editor/src/Tests/EditorFileUsageTest.php- 'body' => array(
modules/editor/src/Tests/EditorFileUsageTest.php- 'value' => $body_value,
modules/editor/src/Tests/EditorFileUsageTest.php- 'format' => 'filtered_html',
--
Note how 'body' now becomes an array with a 'value' and a 'format'.
Best way to create node in Drupal 8 via using core services
$node = \Drupal::entityTypeManager()->getStorage('node')->create([
'type' => 'content_type_machine_name',
'field_text' => 'Foo',
'title' => 'Text Title',
]);
$node->save();
Figured it out. For anyone else with this issue, nodes are now treated as entities, and the entity module is now part of core. So my code ended up looking like this:
$new_page_values = array();
$new_page_values['type'] = 'my_content_type';
$new_page_values['title'] = $form_state['values']['page_title'];
$new_page_values['path'] = $new_page_path;
$new_page = entity_create('node', $new_page_values);
$new_page->save();

Imitating file upload in a form while testing with CakePHP

I'm developing a PHP application where I have a form in which the user should upload a text file and an other page works with this data. I'm also creating controller tests with CakePHP and PHPUnit. My question is, how can I make the test automatically upload a file for the tests of this action?
Thanks in advance.
update 1:
The question in more details: Basically I have a 'submit' action which has the form in its view which submits the parameters (and the uploaded file) to the 'curl' action. Here this curl action processes the uploaded text file and this is actually what I'd like to test. But to test this, I should have an uploaded file with its content, so a more specific question: how could I mock this uploaded file to 'submit' it to my curl controller?
Code snippet for the View:
...
echo '<fieldset><table>';
echo $this->Form->create('Job', array('type' => 'post','action' => 'curl', 'enctype' => 'multipart/form-data'));
echo $this->Form->input('user.name', array('type' => 'hidden', 'value' => $this->User->getCode1(),'name' => 'user.name'));
echo $this->Form->input('class', array('type' => 'hidden', 'value' => $this->Hadoop->getClass(), 'name' => 'class'));
...
update 2:
The test I've already written:
public function testCurl() {
$data = array(
'user_name' => ...,
'release' => ...,
'analysis_period_start' => ...,
'uploaded_file' => ???
);
$Jobs = $this->generate('Jobs', array('components' => array('Session')));
$Jobs->Session->expects($this->any())->method('setFlash');
$this->testAction('/jobs/curl', array('data' => $data, 'method' => 'post'));
}
So basically I'm trying to test my curl action with a POST method with the data provided in the $data variable. But I don't know how to mock/imitate an uploaded file into that array.
update 3:
The relevant cod snippet from my controller's given action:
public function curl() {
/* This action accepts only POST request */
if (!$this->isPOSTRequest())
return $this->redirect(array('controller' => 'jobs', 'action' => 'submit'));
/* Create a new entry in the database and get its ID */
$id = $this->createNewEntryInTheDatabase();
/* Inserts the new patterns into the DB and looks up the already existing
** patterns in the DB. Returns an array with the IDs of the submitted patterns */
$patternIds = $this->lookupPatternIDsAndInsertNewPatterns($_FILES['patterns']);
$curl = $this->initCURL();
...
$this->closeCURL($curl);
return $this->redirect(array('controller' => 'jobs', 'action' => 'submit'));
}
...
private function lookupPatternIDsAndInsertNewPatterns($patternsFile) {
$patternIDs = null;
/* One element for each row */
$patternsArray = $this->convertUploadedCSVFileIntoArray(
$patternsFile, $this->CSV->getDelimiter(),
$this->CSV->getEnclosure(), $this->CSV->getEscape());
...
return $patternIDs;
}
/* Returns the patterns from the CSV file in an array */
private function convertUploadedCSVFileIntoArray(
$patternsFile, $delimiter, $enclosure, $escape) {
$patternsArray = file($patternsFile['tmp_name']);
$patterns = null;
foreach ($patternsArray as $pattern)
$patterns[] = str_getcsv(
$pattern,
$delimiter,
$enclosure,
$escape);
return $patterns;
}

Upload video to YouTube as unlisted

So, I am able to upload a video to YouTube (direct upload) using the PHP client library and set it to private, but is it possible to set it as unlisted?
You must use this code as a child of the XML element of the request:
<yt:accessControl action="list" permission="denied"/>
If you can't add it manually (usually using zend) you may use this code to add the corresponding zend entry:
//Creates an extension to Zend Framework
$element = new Zend_Gdata_App_Extension_Element('yt:accessControl', 'yt', 'http://gdata.youtube.com/schemas/2007', '');
//Adds the corresponding XML child/attribute
$element->extensionAttributes = array(array('namespaceUri' => '', 'name' => 'action', 'value' => 'list'), array('namespaceUri' => '', 'name' => 'permission', 'value' => 'denied'));
//Adds this extension to you video entry where "$myVideo" is your video to be uploaded
$myVideo->extensionElements = array($element);
Hope this helps :D
Do that.. with API ver 2 and ZEND GDATA.
If you look at the content of a $videoEntry you will note a
$_extensionElements and $_extensionArributes.
So looking backwards from the extended class of VideoEntry
you will found the abstract class Zend_Gdata_App_Base
and it has a function setExtensionElements(array).
So only do what others say to create the accesControlElement
and pass it to that function..
And IT WORKS.
$videoEntry = $yt->getFullVideoEntry($id);
if ($videoEntry->getEditLink() !== null) {
echo "<b>Video is editable by current user</b><br />";
$putUrl = $videoEntry->getEditLink()->getHref();
//set video to unlisted
$accessControlElement = new Zend_Gdata_App_Extension_Element(
'yt:accessControl', 'yt', 'http://gdata.youtube.com/schemas/2007', ''
);
$accessControlElement->extensionAttributes = array(
array(
'namespaceUri' => '',
'name' => 'action',
'value' => 'list'
),
array(
'namespaceUri' => '',
'name' => 'permission',
'value' => 'denied'
));
// here is the hidden function
// it´s on a abstract class Zend/Gdata/App/Base/Base.php
// Where ZEND/Gdata/Youtube/VideoEntry.php extends
$videoEntry->setExtensionElements(array($accessControlElement));
$yt->updateEntry($videoEntry, $putUrl);
}else{
echo "<b>EL Video no es editable por este usuario</b><br />";
}

Categories