how to implement placeholder tokens in drupal - drupal

How can one insert a token into a textarea?
There is a token insert module, but that does not have a stable version out yet

taken from drupal.org
hook_token_values($type, $object = NULL, $options = array())
This function should return a keyed array of placeholders, and their replacement values. $type contains the current context -- 'node', 'user', 'global', etc. $object contains the specific node, user, etc. that should be used as the basis for the replacements. Only generate and return replacement tokens when $type is something that your module can really deal with. That helps keep things speedy and avoid needlessly
searching for jillions of replacement tokens. The $options array can contain additional options (exact use is dynamic and not easily documented).
For example:
function my_user_token_values($type, $object = NULL, $options = array()) {
if ($type == 'user') {
$user = $object;
$tokens['name'] = $user->name;
$tokens['mail'] = $user->mail;
return $tokens;
}
}

Related

Changed user password programmatically by username

I'm am trying to change the password of a given user by their username in a drupal 9 module but I keep getting this error:
Error: Call to a member function setPassword() on array in _password_change()
This is the function I am using:
$userName = 'user1';
$password = 'Password1';
$nid = '1';
function _password_change($userName, $password) {
$user_storage = \Drupal::EntityTypeManager()->getStorage('user');
$user = $user_storage->loadByProperties(['name' => $userName]);
$user->setPassword($password);
$user->save();
}
If I use $user = $user_storage->load($nid); instead of $user = $user_storage->loadByProperties(['name' => $userName]); the code runs fine and the password gets applied successfully unfortunely, the given information will be a username and not the entity id.
The $userName , $password and $nid are set manually for testing proposes.
For what I can tell if I call it using the load id i get back an object but if i call it using the loadByProperties i get back and array hence it can't apply the setPassword function.
What would be a way to load the entity object by the username as an object and be able to apply the new password?
loadByProperties returns an array of entity objects.
So you want call setPassword on the first item in the array which should be your user object.
While you are there, you should also probably check that there was a user with the given username by checking the length of the array returned by loadByProperties.
function _password_change($userName, $password) {
$user_storage = \Drupal::EntityTypeManager()->getStorage('user');
$users = $user_storage->loadByProperties(['name' => $userName]);
// check we got 1 (only 1) user
if (count($users) == 1) {
//get the user from the array.
$user = reset($users);
$user->setPassword($password);
$user->save();
}
}
This code is untested, but you get the idea.

Modify multiple entities with one form (bulk edit)

I want to be able to submit a form that modifies multiple entities. Let's say I've got a BulkeditFormType to change the 'active' (Boolean) or 'organisation' (EntityType with App\Entity\Organisation) fields on multiple users at once.
This is my current solution (semi-pseudo-code):
public function bulkedit(Request $request)
{
$form = $this->createForm(BulkeditFormType::class, null, [
//..options
]);
if ($request->isXmlHttpRequest()) {
// fields '_token' and empty values are unset, not shown in this example
$formData = $request->request->get($form->getName());
$entityManager = $this->getDoctrine()->getManager();
$repo = $entityManager->getRepository(App\Entity\User::class);
$entities = $repo->findBy([
'id' => [1,2,3,4,5]
]);
foreach ($entities as $entity) {
$form = $this->createForm(BulkeditFormType::class, $entity, [
//..options
]);
$clearMissing = false;
$form->submit($formData, $clearMissing);
$entityManager->persist($entity);
}
$entityManager->flush();
}
return $this->render('#User/User/bulkedit.html.twig', [
'form' => $form->createView()
]);
}
Note that I've tried to include only relevant parts of my code, so please consider this as pseudo-code, just to get an idea of my current implementation.
While this solution works, it creates a Form object for every entity. Editing 100 users will lead to a large amount of memory usage and many useless database queries.
How can I modify my code in such a way that only one form will be generated which can be re-used by all entities? I've tried to use $form->setData(), but I feel like there must be a better way. A CollectionType will create multiple subforms instead of multiple forms, so in this case it doesn't make a big difference.

SilverStripe translate fieldlabels

I simply use _t() to translate CMS Fields in a DataObject: TextField::create('Title', _t('cms.TitleField', 'Title'));. I thought translating $summary_fields was just as simple, but it's not.
Instead of trying to translate Fields and their accompanying summary_fields seperately, I believe I noticed a better way how these fields are translated using the function FieldLabels as used in SiteTree.
Is there way I can translate these both fields in one place (DRY principle) and apply to both easily by calling the var?
Yes I would certainly say the use of FieldLabels is for localisation / translation because of the comment "Localize fields (if possible)" here in the DataObject code...
public function summaryFields() {
$fields = $this->stat('summary_fields');
// if fields were passed in numeric array,
// convert to an associative array
if($fields && array_key_exists(0, $fields)) {
$fields = array_combine(array_values($fields), array_values($fields));
}
if (!$fields) {
$fields = array();
// try to scaffold a couple of usual suspects
if ($this->hasField('Name')) $fields['Name'] = 'Name';
if ($this->hasDatabaseField('Title')) $fields['Title'] = 'Title';
if ($this->hasField('Description')) $fields['Description'] = 'Description';
if ($this->hasField('FirstName')) $fields['FirstName'] = 'First Name';
}
$this->extend("updateSummaryFields", $fields);
// Final fail-over, just list ID field
if(!$fields) $fields['ID'] = 'ID';
// Localize fields (if possible)
foreach($this->fieldLabels(false) as $name => $label) {
// only attempt to localize if the label definition is the same as the field name.
// this will preserve any custom labels set in the summary_fields configuration
if(isset($fields[$name]) && $name === $fields[$name]) {
$fields[$name] = $label;
}
}
return $fields;
}

Filter ModelAdmin by many_many relation

I'm managing the DataObject class 'trainer' with ModelAdmin. A trainer has a many_many relation to my other class 'language'.
On my 'trainer' class I'm manipulating the 'searchableFields' function to display a ListboxField in the filters area.
public function searchableFields() {
$languagesField = ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
return array (
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => $languagesField
)
);
}
That works like expected and shows me the wanted ListboxField. The Problem is, after selecting 1 or 2 or whatever languages and submitting the form, I'm receiving
[Warning] trim() expects parameter 1 to be string, array given
Is it possible here to filter with an many_many relation? And if so, how? Would be great if someone could point me in the right direction.
Update:
Full Error Message: http://www.sspaste.com/paste/show/56589337eea35
Trainer Class: http://www.sspaste.com/paste/show/56589441428d0
You need to define that logic within a $searchable_fields parameter instead of the searchableFields() which actually constructs the searchable fields and logic.
PHP would be likely to throw an error if you go doing fancy form stuff within the array itself, so farm that form field off to a separate method in the same DataObject and simply call upon it.
See my example, I hope it helps.
/* Define this DataObjects searchable Fields */
private static $searchable_fields = array(
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => self::languagesField()
)
);
/* Return the searchable field for Languages */
public function languagesField() {
return ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
}
Yes, it's possible. You just need to override two methods - one in Trainer data object and one in TrainerModelAdmin. First one will make a field, second one will do filtering.
Trainer Data Object:
public function scaffoldSearchFields($_params = null)
{
$fields = parent::scaffoldSearchFields($_params);
// get values from query, if set
$query = Controller::curr()->request->getVar('q');
$value = !empty($query['Languages']) && !empty($query['Languages']) ? $query['Languages'] : array();
// create a field with options and values
$lang = ListboxField::create("Languages", "Sprachen", Language::get()->map()->toArray(), $value, null, true);
// push it to field list
$fields->push($lang);
return $fields;
}
Trainer Model Admin
public function getList()
{
$list = parent::getList();
// check if managed model is right and is query set
$query = $this->request->getVar('q');
if ($this->modelClass === "Trainer" && !empty($query['Languages']) && !empty($query['Languages']))
{
// cast all values to integer, just to be sure
$ids = array();
foreach ($query['Languages'] as $lang)
{
$ids[] = (int)$lang;
}
// make a condition for query
$langs = join(",", $ids);
// run the query and take only trainer IDs
$trainers = DB::query("SELECT * FROM Trainer_Languages WHERE LanguageID IN ({$langs})")->column("TrainerID");
// filter query on those IDs and return it
return $list->filter("ID", $trainers);
}
return $list;
}

get vocabulary id by name

I can retrieve a vocabulary id directly from DB,
but is there a built in function for this?
for example:
i have a vocabulary called "listing",
i need that built in function takes "listing" as function argument, and return
a vid.
i am using drupal 6
I have a function for this, well almost..
/**
* This function will return a vocabulary object which matches the
* given name. Will return null if no such vocabulary exists.
*
* #param String $vocabulary_name
* This is the name of the section which is required
* #return Object
* This is the vocabulary object with the name
* or null if no such vocabulary exists
*/
function mymodule_get_vocabulary_by_name($vocabulary_name) {
$vocabs = taxonomy_get_vocabularies(NULL);
foreach ($vocabs as $vocab_object) {
if ($vocab_object->name == $vocabulary_name) {
return $vocab_object;
}
}
return NULL;
}
If you want the vid just get the vid property of the returned object and.
$vocab_object = mymodule_get_vocabulary_by_name("listing");
$my_vid = $vocab_object->vid;
Henriks point about storing it in a variable is very valid as the above code you won't want to be running on every request.
Edit
Also worth noting that in Drupal 7 you can use taxonomy_vocabulary_get_names() which makes this a little easier.
For Drupal 7 if you know the vocabulary machine name this is the way:
$vid = taxonomy_vocabulary_machine_name_load('your_vocabulary_name')->vid;
If you know only the Real name of vocabulary, you can use this function:
function _get_vocabulary_by_name($vocabulary_name) {
// Get vocabulary by vocabulary name.
$query = db_select('taxonomy_vocabulary', 'tv');
$query->fields('tv', [
'machine_name',
'vid',
]);
$query->condition('tv.name', $vocabulary_name, '=');
$vocabulary = $query->execute()->fetchObject();
return $vocabulary;
}
There is no built in function for this, afaik. You can roll your own by calling taxonomy_get_vocabularies() and search for your name in the resulting array, but this will do a database request on every call.
If you have a vocabulary that you often use from code, it might be easier/more effective to store the vid in a Drupal variable via variable_set() once and get it back via variable_get() (Many modules that create a vocabulary on install do it this way).
Edit: here is some sample code to do this on module install.
function mymodule_install() {
$ret = array();
$vocabulary = array(
'name' => t('myvocab'),
'multiple' => '1',
'required' => '0',
'hierarchy' => '1',
'relations' => '0',
'module' => 'mymodule',
'nodes' => array('article' => 1),
);
taxonomy_save_vocabulary($vocabulary);
$vid = $vocabulary['vid'];
variable_set('mymodule_myvocab', $vid);
return $ret
}
Should help.
function _my_module_vid($name) {
$names = taxonomy_vocabulary_get_names();
return $names[$name]->vid;
}
You know the node type to which the vocaulbary is associated. So just use taxonomy_get_vocabularies() and pass the node type as argument and you will get the details you want!
In Drupal 7 you could use:
$vocab_object = taxonomy_vocabulary_machine_name_load('vocabulary_name');
$my_vid = $vocab_object->vid;

Resources