Drupal: process order change/ how do I overwrite password suggestion tips - drupal

I want to overwrite the password suggestion tip that's configured in Drupal core to show
'Make it at least 14 characters' from 'Make it at least 12 characters'.
Or simply remove the whole password suggestion tips box.
This is the Drupal core code that I want to overwrite https://git.drupalcode.org/project/drupal/-/blob/9.5.0/core/modules/user/user.module#L1109
Below is my function I wrote in my module.
function mymodule_element_info_alter(array &$types) {
if (isset($types['password_confirm'])) {
$types['password_confirm']['#process'][] = 'mymodule_form_process_password_confirm';
}
}
function mymodule_form_process_password_confirm($element, $form_state) {
if (\Drupal::config('user.settings')->get('password_strength')) {
$password_settings['showStrengthIndicator'] = FALSE;
$password_settings += [
'tooShort' => t('Make it at least 14 characters'),
'username' => \Drupal::currentUser()->getAccountName(),
];
}
return $element;
}
And this is the result I get if I print out the elements
[#process] => Array
(
[0] => Array
(
[0] => Drupal\Core\Render\Element\PasswordConfirm
[1] => processPasswordConfirm
)
[1] => mymodule_form_process_password_confirm
[2] => user_form_process_password_confirm
[3] => password_policy_check_constraints_password_confirm_process
)
I think the problem is that the my module's function gets run after the core so it's doing the opposite of what I want it to do.
Do you know how I can run my function after the core?
Is there other ways to do it?
Or how to remove the whole password suggestion tips from the core?

In your code you're setting the variable $password_settings, and returning $element..... you have not changed $element at all.
try something like this (untested)..
function mymodule_form_process_password_confirm($element, $form_state) {
if (isset($element['#attached']['drupalSettings']['password'])) {
$element['#attached']['drupalSettings']['password']['showStrengthIndicator'] = FALSE;
$element['#attached']['drupalSettings']['password']['tooShort'] = t('Make it at least 14 characters')
}
return $element;
}

Your module just run before the core.
[1] => mymodule_form_process_password_confirm <== yours
[2] => user_form_process_password_confirm <== core
[3] => password_policy_check_constraints_password_confirm_process <== password_policy module
The core's module weight is 0, and it has name 'user' is sorted after your module (mymodule).
So, add a mymodule.install and set the weight of your module higher than 0 (override core) or higher than 10 (override password_policy also)
The function you need to call is module_set_weight, refer to https://api.drupal.org/api/drupal/core%21includes%21module.inc/function/module_set_weight/9.0.x
Your mymodule.install should be something like:
<?php
/**
* Implements hook_install().
*/
function mymodule_install() {
// Your desired weight, 11 is override the password_policy, or just 1 to override core.
module_set_weight('mymodule', 11);
}

Related

User(Serializable) must not be accessed before initialization symfony

When i try to connect as a user (my user entity implement UserInterface), i always get this error:
Typed property Symfony\Component\Security\Core\Exception\AccountStatusException::$user must not be accessed before initialization
At: D:\cours\symfony\blog\vendor\symfony\security-core\Exception\AccountStatusException.php:45
So i implement *Serializable* like someone says it here: https://github.com/symfony/symfony/issues/38274#issuecomment-697231222 like this:
public function serialize(): array {
return ['id' => $this->getId(), 'email' => $this->getEmail(), 'password' => $this->getPassword(), 'roles' => $this->getRoles()];//FIXME ajouter rôle?
}
public function unserialize($serialized): void {
list($this->id, $this->name, $this->email, $this->roles) = unserialize($serialized);
}
But i still get "User must not be accessed before initialization". 🤔 So maybe implementing \Serializable is the old way to do it (from 2020).
I just need to update all my bundle. Like my last edit suggested: Somes of my bundles was too old.

Sonata Admin => Select just the given Discriminator Map type

Subject
When I have a set of entities with a Doctrine Discriminator Map then I cannot add a filter to get just one type of all mapped entities, due SonataAdminBundle and/or SonataDoctrineORMAdminBundle are throwing an error.
Example:
Entities with a Doctrine Discriminator Map
/**
* #ORM\Table(name="activities")
* #ORM\Entity()
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({
* "joined" = "...\JoinedActivity",
* "other" = "...\OtherActivity"
* })
*/
abstract class Activity()
{
abstract public function getType();
}
/**
* #ORM\Entity()
*/
class JoinActivity extends Activity()
{
const TYPE = 'joined';
public function getType()
{
return self::type;
}
}
/**
* #ORM\Entity()
*/
class OtherActivity extends Activity()
{
const TYPE = 'other';
public function getType()
{
return self::type;
}
}
Then I add the Sonata Admin filter:
protected function configureDatagridFilters(DatagridMapper $filter)
{
$filter->add(
'type',
null,
[
'label' => 'Activity Type',
],
'choice',
[
'choices' => [
JoinActivity::TYPE => ucfirst(JoinActivity::TYPE),
OtherActivity::TYPE => ucfirst(OtherActivity::TYPE),
],
]
);
}
Expected results
Get a new filter to select just joined or other activities.
Actual results
Notice: Undefined index: type
500 Internal Server Error - ContextErrorException
Stack trace
As requested by greg0ire this is the Stack Trace returned by Symfony/Sonata:
[1] Symfony\Component\Debug\Exception\ContextErrorException: Notice: Undefined index: type
at n/a
in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php line 69
at Symfony\Component\Debug\ErrorHandler->handleError('8', 'Undefined index: type', '/path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php', '69', array('class' => 'AppBundle\EntityBundle\Entity\Activity', 'property' => 'type', 'modelManager' => object(ModelManager), 'ret' => array(object(ClassMetadata), 'type', array()), 'options' => array('field_type' => null, 'field_options' => array(), 'options' => array(), 'parent_association_mappings' => array()), 'metadata' => object(ClassMetadata), 'propertyName' => 'type', 'parentAssociationMappings' => array()))
in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php line 69
at Sonata\DoctrineORMAdminBundle\Guesser\FilterTypeGuesser->guessType('AppBundle\EntityBundle\Entity\Activity', 'type', object(ModelManager))
in /path/to/symfony/project/app/cache/dev/classes.php line 15104
at Sonata\AdminBundle\Guesser\TypeGuesserChain->Sonata\AdminBundle\Guesser\{closure}(object(FilterTypeGuesser))
in /path/to/symfony/project/app/cache/dev/classes.php line 15111
at Sonata\AdminBundle\Guesser\TypeGuesserChain->guess(object(Closure))
in /path/to/symfony/project/app/cache/dev/classes.php line 15105
at Sonata\AdminBundle\Guesser\TypeGuesserChain->guessType('AppBundle\EntityBundle\Entity\Activity', 'type', object(ModelManager))
in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Builder/DatagridBuilder.php line 105
at Sonata\DoctrineORMAdminBundle\Builder\DatagridBuilder->addFilter(object(Datagrid), null, object(FieldDescription), object(ActivityAdmin))
in /path/to/symfony/project/app/cache/dev/classes.php line 13069
at Sonata\AdminBundle\Datagrid\DatagridMapper->add('type', null, array('label' => 'Activity Type', 'field_options' => array('choices' => array('joined' => 'Joined')), 'field_type' => 'choice', 'field_name' => 'type'), 'choice', array('choices' => array('joined' => 'Joined')))
in /path/to/symfony/project/src/AppBundle/SonAdminBundle/Admin/ActivityAdmin.php line 64
at AppBundle\SonAdminBundle\Admin\ActivityAdmin->configureDatagridFilters(object(DatagridMapper))
in /path/to/symfony/project/app/cache/dev/classes.php line 10609
at Sonata\AdminBundle\Admin\AbstractAdmin->buildDatagrid()
in /path/to/symfony/project/app/cache/dev/classes.php line 10910
at Sonata\AdminBundle\Admin\AbstractAdmin->getDatagrid()
in /path/to/symfony/project/vendor/sonata-project/admin-bundle/Controller/CRUDController.php line 104
at Sonata\AdminBundle\Controller\CRUDController->listAction()
in line
at call_user_func_array(array(object(CRUDController), 'listAction'), array())
in /path/to/symfony/project/app/bootstrap.php.cache line 3222
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), '1')
in /path/to/symfony/project/app/bootstrap.php.cache line 3181
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), '1', true)
in /path/to/symfony/project/app/bootstrap.php.cache line 3335
at Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(object(Request), '1', true)
in /path/to/symfony/project/app/bootstrap.php.cache line 2540
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
in /path/to/symfony/project/web/app_dev.php line 15
at require('/path/to/symfony/project/web/app_dev.php')
in /path/to/symfony/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php line 40
Any idea how I can fix it?
Thanks,
I face the same issue. I did a work around - I use doctrine_orm_callback type with doctrine INSTANCE OF operator.
Code looks like this:
->add('userType',
'doctrine_orm_callback',
[
'callback' => function ($queryBuilder, $alias, $field, $value) {
if (!is_array($value) || !array_key_exists('value', $value) || empty($value['value'])) {
return false;
}
$queryBuilder->andWhere($alias . ' INSTANCE OF :userType');
$queryBuilder->setParameter('userType', $value['value']);
return true;
},
],
ChoiceType::class,
[
'choices' => array_flip(UserType::getChoices()),
'translation_domain' => $this->getTranslationDomain(),
]
)
And it's working. Maybe it helps you.
I think Sonata is confused b/c it expects type to appear in the fields in your Doctrine mapping. I don't think filtering by type is supporting, but I recall having some support for single inheritance, especially when creating new objects. Can't find it back though.
It is not recommanded to modify the link between an inherited entity and his superclass once it is created. If you need it, you should consider using composition instead.
That's why Doctrine does not allow to directly manage the discriminator field.
All the following text is copied from this very useful response which is precious for a global and better understanding.
It is not a good sign when the type of an instance of an object needs to change over time. I'm not talking about downcasting/upcasting here, but about the need to change the real type of an object.
First of all, let me tell you why it is a bad idea:
A subclass might define more attributes and do some additionnal work
in it's constructor. Should we run the new constructor again? What
if it overwrites some of our old object's attributes?
What if you were working on an instance of that Person in some part of your code, and then it suddenly transforms into an Employee (which might have some redefined behavior you wouldn't expect)?!
That is part of the reason why most languages will not allow you to change the real class type of an object during execution (and memory, of course, but I don't want to get into details). Some let you do that (sometimes in twisted ways, e.g. the JVM), but it's really not good practice!
More often than not, the need to do so lies in bad object-oriented design decisions.
For those reasons, Doctrine will not allow you to change the type of your entity object. Of course, you could write plain SQL (at the end of this post - but please read through!) to do the change anyway, but here's two "clean" options I would suggest:
I realize you've already said the first option wasn't an option but I spent a while writing down this post so I feel like I should make it as complete as possible for future reference.
Whenever you need to "change the type" from Person to Employee, create a new instance of the Employee and copy the data you want to copy over from the old Person object to the Employee object. Don't forget to remove the old entity and to persist the new one.
Use composition instead of inheritance (see this wiki article for details and links to other articles). EDIT: For the hell of it, here's a part of a nice conversation with Erich Gamma about "Composition over Inheritance"!
See related discussions here and here.
Now, here is the plain SQL method I was talking about earlier - I hope you won't need to use it!
Make sure your query is sanitized (as the query will be executed without any verification).
$query = "UPDATE TABLE_NAME_HERE SET discr = 'employee' WHERE id = ".$entity->getId();
$entity_manager->getConnection()->exec( $query );
Here is the documentation and code for the exec method which is in the DBAL\Connection class (for your information):
/**
* Execute an SQL statement and return the number of affected rows.
*
* #param string $statement
* #return integer The number of affected rows.
*/
public function exec($statement)
{
$this->connect();
return $this->_conn->exec($statement);
}

get list of network enabled themes in wordpress multisite installation

I am working on a theme, where I want to get to know if the theme is network enabled or not for some functionality.
Can anyone explain me how can I get the list of network enabled themes? or just to know a theme is network enabled or not?
Any help is appreciated.
How about a 7.5 year too late answer?
I'm writing a plugin that needed this functionality too.
Unfortunately I couldn't find any clearly defined "network-enabled" functions, hooks, or DB table keys related to themes. Plugins get a little bit more love in that regard.
DB Info
With that said, network-activated plugins are stored in the main wp_sitemeta table with the key "allowedthemes".
Unfortunately, (yet again), it's not a consistent array ready for use as-is.
It contains EVERY theme "slug" as an array value with standard number index keys, but ALSO contains the network-activated themes with the theme "slug" as the key and a boolean "1" as the value. Why? I have no idea, but surely it made sense to someone, somewhere, at some point in time.
DB Serialized Meta Value
Example meta_value for meta_key "allowedthemes" in wp_sitemeta table in the DB:
a:8:{i:0;s:12:"twentytwenty";i:1;s:15:"twentytwentyone";i:2;s:17:"twentytwentythree";i:3;s:15:"twentytwentytwo";s:12:"twentytwenty";b:1;s:15:"twentytwentyone";b:1;s:17:"twentytwentythree";b:1;s:15:"twentytwentytwo";b:1;}
Retrieving Values
Getting this value depends on the type of multisite, and/or plugin compatibility you want to offer.
For single network multisite installs
$allowed_themes = get_site_option('allowedthemes');
For multinetwork multisite installs
$allowed_themes = get_network_option(get_current_network_id(), 'allowedthemes');
Results
echo '<pre>',print_r($allowed_themes),'</pre>';
// shows
Array
(
[0] => twentytwenty
[1] => twentytwentyone
[2] => twentytwentythree
[3] => twentytwentytwo
[twentytwenty] => 1
[twentytwentyone] => 1
[twentytwentythree] => 1
[twentytwentytwo] => 1
)
1
Results Breakdown
Array values with [#] => theme_slug are just installed/available themes.
Array values with [theme_slug] => 1 are network-activated themes.
Again, WHY mix them in one array like this? Couldn't tell you. It is what it is.
Making Results Useful
Now there's plenty of ways to extract JUST the network activated themes, or JUST the installed/available themes with array_walk functions and other techniques.
One (less elegant, but more thorough) way I do this within the plugin I'm writing is to loop through all the themes from wp_get_themes() and wherever a "theme slug" is the key, append it to an array for later use:
$all_themes = wp_get_themes();
$allowed_themes = get_site_option('allowedthemes');
foreach ($all_themes as $theme => $object) {
if (isset($allowed_themes[$theme]) && (bool) $allowed_themes[$theme]) {
$network_activated_themes[] = $theme;
}
}
echo '<pre>',print_r($network_activated_themes),'</pre>';
// shows
Array
(
[0] => twentytwenty
[1] => twentytwentyone
[2] => twentytwentythree
[3] => twentytwentytwo
)
1
/* ALTERNATE LOOP TO GET THEME OBJECT DATA */
foreach ($all_themes as $theme => $object) {
if (isset($allowed_themes[$theme]) && (bool) $allowed_themes[$theme]) {
$network_activated_themes[$theme] = $object;
}
}
echo '<pre>',print_r($network_activated_themes),'</pre>';
// shows an array identical to wp_get_themes(), but only with network-activated themes
Once you have an array of JUST network-activated themes like this, you should be able to accomplish your goals with network-activated themes.
I realize that #nitin-yawalkar probably doesn't need this help anymore, but due to the complete lack of answers here and elsewhere related to this question, I wanted to chime in and add SOMETHING to help steer people in the right direction.
UPDATE
I did find a filter of interest for this.
apply_filters( 'allowed_themes', string[] $allowed_themes )
https://developer.wordpress.org/reference/hooks/allowed_themes/
It's not very helpful as-is, but within proper context/scope, it's quite handy.
Example 'allowed_themes' Filter on wp-admin/themes.php page
One aspect of the plugin I'm developing allows setting allowed themes on a per-site basis on multisites. This filter is network-wide, so to apply it on a per-site basis you can do something like this:
add_filter('allowed_themes', 'tqc_show_allowed_themes');
function tqc_show_allowed_themes( $allowed_themes ) {
// make sure we're in the admin panel
if ( is_admin() ) {
// make sure we're on the themes.php page
global $pagenow;
if ( $pagenow === 'themes.php' ) {
//print the standard array
echo '<pre>',print_r( $allowed_themes ),'</pre>';
/*
* shows same array as
* get_site_option( 'allowedthemes' );
* and
* get_network_option( get_current_network_id(), 'allowedthemes' );
*
Array
(
[0] => twentytwenty
[1] => twentytwentyone
[2] => twentytwentythree
[3] => twentytwentytwo
[twentytwenty] => 1
[twentytwentyone] => 1
[twentytwentythree] => 1
[twentytwentytwo] => 1
)
1
*
*/
// a separate custom function that retrieves valid themes
// allowed to be used by the current site
$valid_themes = tqc_get_valid_themes( get_current_blog_id() );
// set allowed themes to empty
$allowed_themes = array();
// loop through once to build out the [#] => theme_slug part
foreach( $valid_themes as $theme => $values ) {
$allowed_themes[] = $theme;
}
// loop through again to build out the [theme_slug] => 1 part
foreach( $valid_themes as $theme => $values ) {
$allowed_themes[ $theme ] = (bool) true;
}
}
}
// print the new array
echo '<pre>',print_r( $allowed_themes ),'</pre>';
/*
* shows modified array of allowed themes for this specific site
*
Array
(
[0] => twentytwenty
[1] => twentytwentyone
[twentytwenty] => 1
[twentytwentyone] => 1
)
1
*
*/
// return array to the filter to be applied
return $allowed_themes;
}
The 'allowed_themes' filter applies network-wide. Use context/scope like the example above to make it useful per-site / per-role / per-user, and limit it so that it's only applied when / where you need to alter the allowed themes.

symfony2 get all validation constraints on an entity (yml, xml, annotations)

Im trying to get all validation constraints on an entity and translate theses constraints to Jquery validation rules, right now im able to get annotation defined constraints (thanks to : Symfony2 get validation constraints on an entity), but im having some trouble getting xml and yml ones.
$xml_file_loader = new XmlFileLoader("path_to_my_project/vendor/friendsofsymfony/user-bundle\FOS\UserBundle\Resources\config\validation.xml");
Using a similar code means that i need to know beforehand where the xml/yml file is located, i m trying to write somehow a generic code that can do this automatically.
Isn't there a way to get all constraints at once? if not how can i know the location of xml/yml files, and also in cases of inheritance i need to check for parent constraints... Is this doable?
private function getValidations()
{
$validations=[];
$validator=$this->get("validator");
$metadata=$validator->getMetadataFor(new your_entity());
$constrainedProperties=$metadata->getConstrainedProperties();
foreach($constrainedProperties as $constrainedProperty)
{
$propertyMetadata=$metadata->getPropertyMetadata($constrainedProperty);
$constraints=$propertyMetadata[0]->constraints;
$outputConstraintsCollection=[];
foreach($constraints as $constraint)
{
$class = new \ReflectionObject($constraint);
$constraintName=$class->getShortName();
$constraintParameter=null;
switch ($constraintName)
{
case "NotBlank":
$param="notBlank";
break;
case "Type":
$param=$constraint->type;
break;
case "Length":
$param=$constraint->max;
break;
}
$outputConstraintsCollection[$constraintName]=$param;
}
$validations[$constrainedProperty]=$outputConstraintsCollection;
}
return $validations;
}
Returns:
array(13) (
[property1] => array(4) (
[NotBlank] => (string) notBlank
[NotNull] => (string) notBlank
[Type] => (string) string
[Length] => (int) 11
)
[property2] => array(4) (
[NotBlank] => (string) notBlank
[NotNull] => (string) notBlank
[Type] => (string) string
[Length] => (int) 40
)
..........
)
The returned array can be configured or used to define client side validation rules depending on the client-side validation library/code that you are using
$validator=$this->get("validator");
$metadata=$validator->getMetadataFor(new yourentity());
The object $metadata now contains all the metadata about validations that concerns your specific entity.

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