Drupal Views exposed filter of Author name as a drop down - drupal

This is a follow up question to Drupal Views exposed filter of Author name. The following question was answered and works. I can filter a view by user name. The user name is entered is entered by typing in a box and the box then auto completes. Rather then doing this I would like the list of users as a drop down. I only need one user to be selected. Do you know if this is possible?

You'll need a custom module for that.
I've done this for Drupal 7 this way: create a module, say, views_more_filters, so you have a views_more_filters.info file like this:
name = Views More Filters
description = Additional filters for Views.
core = 7.x
files[] = views_more_filters_handler_filter_author_select.inc
files[] = views_more_filters.views.inc
(file views_more_filters_handler_filter_author_select.inc will contain our filter handler).
A basic views_more_filters.module file:
<?php
/**
* Implements of hook_views_api().
*/
function views_more_filters_views_api() {
return array('api' => 3);
}
Then define your filter in views_more_filters.views.inc:
<?php
/**
* Implements of hook_views_data().
*/
function views_more_filters_views_data() {
return array(
'node' => array(
'author_select' => array(
'group' => t('Content'),
'title' => t('Author UID (select list)'),
'help' => t('Filter by author, choosing from dropdown list.'),
'filter' => array('handler' => 'views_more_filters_handler_filter_author_select'),
'real field' => 'uid',
)
)
);
}
Note that we set author_select as a machine name of the filter, defined filter handler ('handler' => 'views_more_filters_handler_filter_author_select') and a field we will filter by ('real field' => 'uid').
Now we need to implement our filter handler. As our filter functions just like default views_handler_filter_in_operator, we simply extend its class in views_more_filters_handler_filter_author_select.inc file:
<?php
/**
* My custom filter handler
*/
class views_more_filters_handler_filter_author_select extends views_handler_filter_in_operator {
/**
* Override parent get_value_options() function.
*
* #return
* Return the stored values in $this->value_options if someone expects it.
*/
function get_value_options() {
$users_list = entity_load('user');
foreach ($users_list as $user) {
$users[$user->uid] = $user->name;
}
// We don't need Guest user here, so remove it.
unset($users[0]);
// Sort by username.
natsort($users);
$this->value_options = $users;
return $users;
}
}
We haven't had to do much here: just populate options array with a list of our users, the rest is handled by parent class.
For further info see:
Views API
Where can I learn about how to create a custom exposed filter for Views 3 and D7? on Drupal Answers
Demystifying Views API - A developer's guide to integrating with Views
Sophisticated Views filters, part2 - writing custom filter handler (in Russian, link to Google translator)
Tutorial: Creating Custom Filters in Views

Yes, this is possible. Its not particularly tough to do this... but its slightly tedious. You need to create two views
The first view is a list of users on your system (a View of type Users). This user list is displayed as a dropdown instead of a list (using jump menu view style). Clicking on any user within this dropdown will call the second view with the uid (user id) of the selected user as the argument in the URL. This view is a block.
The second view is a simple Node listing. It is a page view at a particular URL. It takes 1 argument which is the uid (user id) of the user.
Detailed Steps
Download the Ctools module
http://drupal.org/project/ctools
Enable the Chaos Tools Module. This
module provides a Views Style Plugin
called "Jump Menu"
Create a new view of type Users and NOT type Node which you usually
create. In the fields add User:
Name and User: uid. For the
settings of User: uid, make sure
you click on Rewrite the output of
the field. The rewritten output of
the field should be
my_node_list/[uid]. Make sure you
select the exclude from display checkbox.
In the settings for Style in the view, select the Jump Menu style. Click on the settings for the style. Make sure the Path dropdown has User: uid choosen
Add a block display to the view. Name the block User Drop Down
Save the view
Add the block User Drop Down to any region in your theme e.g. Content Top (usually the best) or left sidebar. Make sure the block is only visible at the urls my_node_list/* and my_node_list by setting the block visibility settings
Now create another view of type Node. Add an argument field User: uid. Add the fields you are interested in e.g. Node: title, User: Name etc.
Add a page display. Let the page be at the url my_node_list
Save the view. Test the dropdown with its list of users on the system at http://yoursitename/my_node_list

http://drupal.org/project/better_exposed_filters
Check this one

I think you just have to choose "Taxonomy:term The taxonomy term ID" instead of "name".

Found a simple solution here. http://bryanbraun.com/2013/08/06/drupal-tutorials-exposed-filters-with-views

Related

Silverstripe 4 - Adding a FormAction via getCMSFields

Goal:
I have a DataObject called "Event". This is in a managed_model for "EventsAdmin" (extending ModelAdmin). When editing an Event, I want a tab on the record called "Moderation" that has a few fields and two buttons: "Approve" and "Reject". These two buttons call an action each that performs relevant actions.
Event extends DataObject
public function getCMSFields() {
$fields = parent::getCMSFields();
$eventStatus = $fields->dataFieldByName("EventStatus")
->setTitle('Current Status')
->setDisabled(true);
$approveButton = FormAction::create('doApproveEvent', _t('SiteBlockAdmin.Approve', 'Approve'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-success font-icon-check-mark-circle');
$rejectButton = FormAction::create('doRejectEvent', _t('SiteBlockAdmin.Reject', 'Reject'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-danger font-icon-cancel-circled');
$fields->addFieldsToTab('Root.Moderation', [
$eventStatus,
$approveButton,
$rejectButton
]);
return $fields;
}
This displays the buttons just fine. But they don't do anything. So I am trying to work out how they can plug into action methods doApproveEvent and doRejectEvent (And where they should go)
I did find docs that led me to adding the buttons to the action bar at the bottom of the CMS page via updateFormActions(). But this isn't what I want as the other fields I am adding above the buttons are part of the Approve/Reject process. Here is the code for this method. This works fine barring the buttons are not in a logical place for the process I'm trying to create.
class CMSActionButtonExtension extends DataExtension
{
public function updateFormActions(FieldList $actions)
{
$record = $this->owner->getRecord();
if (!$record instanceof Event || !$record->exists()) {
return;
}
$approveButton = FormAction::create('doApproveEvent', _t('SiteBlockAdmin.Approve', 'Approve'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-success font-icon-check-mark-circle');
$rejectButton = FormAction::create('doRejectEvent', _t('SiteBlockAdmin.Reject', 'Reject'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-danger font-icon-cancel-circled');
$actions->push($approveButton);
$actions->push($rejectButton);
}
public function doApproveEvent($data, $form) {
$record = $this->owner->getRecord();
// Approve logic
}
public function doRejectEvent($data, $form) {
$record = $this->owner->getRecord();
// Reject logic
}
}
The above Extension is attached to GridFieldDetailForm_ItemRequest
extension.yml
SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest:
extensions:
- My\Namespace\CMSActionButtonExtension
Interestingly, if I have both sets of buttons on the page at the same time, the updateFormActions option works while my desired option still doesn't. Despite the buttons being of identical markup and sitting inside the exact same form tag. I assume that has something to do with how Silverstripe loads the main content panel and the DOM.
Any thoughts on achieving this? Anyone seen a button added to the main CMS panel in a module that I could take a look at? I found this post from 5 years ago, but it's for SS3 and the answer doesn't work for me.
Short answer:
you have to add custom FormActions through an Extension on the Controller that controls the form (or on the form itself
Long Answer:
A bit of background on how SilverStripe does forms:
Generally speaking, forms are always served through Controllers/RequestHandlers (they need to be accessible on some route, usually that's an Action on a Controller that is often named Form, EditForm, ItemEditoForm, ...).
Fields
Inside the CMS you rarely ever have to create your own form, that's done by the CMSs built in Controllers/RequestHandlers for the admin area (GridFieldDetailForm_ItemRequest in this case).
Basically (pseudo code here), what those controllers do is:
public function EditForm() {
$fields = $myCurrentlyEditingDataObject->getCMSFields();
$actions = ...;
$validator = ...;
$this->updateFormActions(&$actions);
$form = new Form('ItemRequestForm', $fields, $actions, $validator);
$this->updateItemEditForm(&$form); // or $this->updateEditForm()
return $form;
}
So, getCMSFields() and in some cases getCMSActions()/getCMSValidator() (not sure if those 2 are still used in SilverStripe 4.x), you can add things to the form, without ever seeing the form object.
Also, the getCMSFields() will always be put into the ``` section of the Form, that's why your button is somewhere in the middle with all the fields and not with the other actions.
Submission
When a form is submitted (eg to /admin/pages/edit/EditForm/265/field/NameOfMyGridField/item/542/ItemEditForm), it will call the action GridFieldDetailForm_ItemRequest->ItemEditForm() which returns the Form object where subsequently FormRequestHandler->httpSubmission() is called. This will then look at the submitted data to figure out what action was clicked (eg $_REQUEST['action_doApproveEvent']) and try to find that action.
The way it tries to find that, is checking if it itself has a method called doApproveEvent, if that fails, it will try Form->getController()->doApproveEvent() or something like that. In the case of a GridField, that controller is GridFieldDetailForm_ItemRequest which means it will try to call GridFieldDetailForm_ItemRequest->doApproveEvent()
So, that means DataObject->getCMSFields() lets you easily add FormFields (and FormActions) into your form body.
But it does not provide a means of adding a method to handle the submission.
That's why, for custom actions you need to modify the Controller (GridFieldDetailForm_ItemRequest in this case).
You are doing this by creating a Extension which you attached to GridFieldDetailForm_ItemRequest.
Any method in your Extension is added to the thing it's attached to, so if you add a method called updateFormActions, it will kind of become GridFieldDetailForm_ItemRequest->updateFormActions().
And if you recall from earlier, the controller will call $this->updateFormActions() during the creation of the form.
Additionally, as I explained earlier, when a FormAction is named doApproveEvent it will look for a GridFieldDetailForm_ItemRequest->doApproveEvent(), which now exists because you added it through that Extension.
So, in summary: you have to add custom FormActions through an Extension on the Controller that controls the form (or on the form itself
PS: the old post from
bummzack you linked to worked in 3.x, because the Controller in his example that created the form was an instance of LeftAndMain.

How can I add a paragraph (as default) in the node form by using hook_form_alter in Drupal 8?

I am trying to add a bunch of different empty paragraphs of different types, to a entity reference revisions field, everytime a node of a certain content type is created.
I DON'T want to use the contrib module "default paragraphs" for this, because I need to use a certain form widget here, and default paragraphs is also achieved by a widget.
What I tried so far:
function myModule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id){
$paragraph = \Drupal\paragraphs\Entity\Paragraph::create([
'type' => 'tab_features'
]);
$paragraph->save();
$form['field_tabs']['widget'][0]['target_id']=$paragraph->id();
$form['field_tabs']['widget'][0]['target_revision_id']=$paragraph->getRevisionId();
return $form;
}
$field_tabs is my entity reference revisions field.
'tab_features' is the paragraphs type I want to add.
I guess there should be a method that can be used in the form or form widget to add a paragraph to the form, like someone already clicked the button to add it. I want to avoid to actually trigger this via Javascript if possible. Anybody knows how to do this in form_alter?
In a project I'm working on, we have done something like this:
//get the body field
$field = $entity->get('field_em_p_body');
$paragraph = Paragraph::create([
'type' => 'em_section', // Paragraph type.
]);
$paragraph->isNew();
$paragraph->set('YOUR_FIELD', 'SOMETHING');
$field->appendItem($paragraph);

Conditionally disable validation for embedded form

I have an embedded form (for Address) which has its own validations for various properties. I embed this form in a parent form (for Person), and I have a checkbox on the parent form that says something like "Person has an address?"
When the checkbox is left unchecked, I want to disable all the validation for the embedded Address form. Or, better yet, if I can just remove the embedded form from being submitted completely that would be OK too.
I looked at using validation groups, but the use case doesn't match my own.
OK, figured this out. When adding the AddressType embedded form in my form builder, I just pass in the option for validation groups like so:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$form->add('address', new AddressType(), array(
'label' => 'Address',
'validation_groups' => function (FormInterface $form) {
if ($form->getParent()->get('toggleAddress')->getData() === false) {
return array();
}
return array('Default');
}
));
});
Within the validation group function, a check is made to see if the toggle to enable Address is off. If so, return a blank array, with removes all validation groups, including the "Default" one.
You try to fix your issue with validation group which will not cover your use case (it can but it will be tricky because en empty Address object will be linked to your Person object).
Basically, you embed your Address form everytime whereas it should only be embed when the checkbox is checked. IMHO, you should rely on dynamic form as explained here.
With this solution, you will need extra JS code in order to update you form when you click the checkbox in order to update the whole form accordingly. Then, there will be no issue about validation because the Address object will only be created when the form is embed.
Additionally (just for information), you can add/edit validation groups according to the submitted data as explained here.
Hope my answer is helpfull!

Drupal: automatically add menu items when new nodes are added

can I automatically add a menu item when I add a node to the page in Drupal?
In other words, can I associate a menu parent with a node content-type, and then automatically add the children if new nodes are added ?
thanks
You can do it with Rules on Drupal 7. This module: http://drupal.org/project/menu_rules adds some actions to rules. One of them is to create a menu item for a node. You select:
Event: Create a node | Update a node
Condition: Content type is "your content type"
Action: Update a menu item for node (there is a checkbox to create the menu item if it doesnt exist)
There's also the Menu Position module that allows to put content under specific menu entries, depending on their content type, their language and taxonomy. It also has a small API to add other criteria.
Yes.
I am sure there is a module do to something like that, but you could also create your own.
There are two ways you could go about it.
You could use hook_menu() to query for the items you want and return the correct menu structure. You would need to also make sure the menu cache is rebuilt on a node save using hook_nodeapi().
See henricks' comments below about why this is a bad idea
Alternitivly you could use hook_nodeapi() to add custom menu items with menu_link_save().
Edit
hook_menu should return an array of menu items, often these are pretty static however there is nothing wrong with these arrays being dynamically generated.
So you can query the node table to get a list of nodes you want, loop through these items and dynamically create an array which contains the correct menu items.
very roughly:
function example_menu() {
$result = db_query('select * from node where ...'); // put in your own select items and where clause
$menu = array();
while ($row = db_fetch_object($result)) {
$menu['my_path/' . $row->nid;] = array(
// See hook menu docs for what to put here.
);
}
return $menu;
}
You should take a look at the Auto Menu module - while the Drupal 6 version is still a dev release, it might cover your needs. If not, you can take it as an example of how to use menu_link_save() to create your own solution.
I would also go for a menu_link_save() call. Together with the Rules module, you can set up an action whenever a new node is saved, to create an appropriate menu item automatically.
You might want to have a look at the tutorial I wrote some time ago, which deals with programatically creating menu items using menu_link_save() and Rules: http://jan.tomka.name/blog/programmatically-creating-menu-items-drupal
Here is case where you can do this....
A node campaign creating menu item 'CAMPAIGN 001' when it is created. Using default_menu_link
Now another content type, 'Sub Campaign' creating a node, using campaign as EntityRef so its menu item should be under the Menu Item of campaign created earlier.
function mymodule_node_insert($node) {
if ($node->type == 'sub-campaign') {
if (isset($node->field_reference_campaign['und'][0]['target_id'])) {
$campaign_node_id = $node->field_photo_album_campaign['und'][0]['target_id'];
$campaign_loaded = node_load($campaign_node_id);
// Get menu link id for the campaign node.
$campaign_node_id_mlid = custom_node_mlid($campaign_node_id);
$campaign_loaded_title = strtolower(str_replace(' ', "-", $campaign_loaded->title));
$campaign_loaded_title_link_path = 'campaign/' . $campaign_loaded_title . '/photo-albums';
//I will query if it exist or not, if not then will create a sub menu item.
$link_exist = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $campaign_loaded_title_link_path))->fetchField();
dsm($link_exist);
if (!$link_exist) {
// Create menu item under campaign.
custom_create_menu_item($campaign_loaded_title_link_path, 'photo-albums', $campaign_node_id_mlid);
//watchdog('glue_site - Menu Item', 'Link Created');
}
else {
//dsm('Link Exist.');
watchdog('glue_site - Menu Item', 'Link Already Exist');
}
}
}
if ($node->type == 'campaign') {
}
}
Then a custom function to create menu item
function custom_create_menu_item($campaign_loaded_title_link_path, $type, $plid) {
switch ($type) {
case 'photo-albums':
$item = array(
'link_path' => $campaign_loaded_title_link_path,
// If changing the title here, change it in template.php as well.
'link_title' => 'Sub Campaign',
'menu_name' => 'menu-campaign-menu', // Menu machine name, for example: main-menu
'weight' => 0,
'plid' => $plid, // Parent menu item, 0 if menu item is on top level
'module' => 'menu',
'router_path' => 'campaign/%/sub-campaign',
'customized' => '1',
);
menu_link_save($item);
menu_cache_clear_all();
watchdog('glue_site - Menu Item', 'Link Created');
break;
}
}
To get the mlid of parent node. Campaign node...
function custom_node_mlid($nid) {
// Require menu node module.
$arr = menu_node_get_links($nid);
$mlid = array_keys($arr);
return $mlid[0];
}
For this you need menu_node
This is a simple problem that unfortunately the Drupal community has decided it wants to make complicated. Forget about all the hacky solutions with rules and hooks. There are two modules, depending on whether you're on Drupal 6 or Drupal 7, that solve the problem very elegantly. I advise against actually creating menu entries. Instead the two modules below dynamically render the nodes in the menu, so that your menu editor doesn't get filled with thousands of nodes. Then, for example, if you decide you want all the blog posts to be moved from [Our Blog] to [About Us]->[News] it's just a mater of changing one setting. No updating thousands of nodes.
D6 Menu Trails
D7 Menu Position
It looks like there's a Drupal module that does this: Auto Menu. Some more details about this module (from its project page):
The Auto Menu module automatically generates menu entries on node creation/edition. Parent menu item can be specified on a per content type basis.
This module acts when the menu section of a node is left empty only. So, users can still organize menus manually. Moreover, default setting for content types is to not create menu items automatically.
Menu Views is an interesting module for Drupal 7 to automatically generate menu links. It allows you to use the power of Views to create menu links and can be used out-of-the-box in combination with modules such as Superfish and Nice Menus.
(PS: my reputation is not high enough to provide more than two links, therefore I have marked the other modules bold instead of providing hyperlinks)

Drupal Views2 Exposed Form how to change

I have a View with an exposed form . I am trying to a few things on it. Ideally I would like to have a dropdown that fires the form with no button. If that is not possible then I would like to have the button text something different than apply.
I hacked it for now and change views_form in views.module but that does not seem like the right way to do it. I only have one exposed form right now, but what if I add more?
Please see http://www.wiredvillage.ca/News for my example.
I am poking around drupal.org and seeing others with the same problem but no solutions so far. Not sure where the best place to get Drupal help is.
Here is the change I made so far:
function views_exposed_form(&$form_state) {
// Make sure that we validate because this form might be submitted
// multiple times per page.
$form_state['must_validate'] = TRUE;
$view = &$form_state['view'];
$display = &$form_state['display'];
$form_state['input'] = $view->get_exposed_input();
// Let form plugins know this is for exposed widgets.
$form_state['exposed'] = TRUE;
$form['#info'] = array();
if (!variable_get('clean_url', FALSE)) {
$form['q'] = array(
'#type' => 'hidden',
'#value' => $view->get_url(),
);
}
// Go through each filter and let it generate its info.
foreach ($view->filter as $id => $filter) {
$view->filter[$id]->exposed_form($form, $form_state);
if ($info = $view->filter[$id]->exposed_info()) {
$form['#info']['filter-' . $id] = $info;
}
}
// I CHANGED The VALUE OF THIS SUBMIT BUTTON TO GO
$form['submit'] = array(
'#name' => '', // prevent from showing up in $_GET.
'#type' => 'submit',
'#value' => t('go'),
);
$form['#action'] = url($view->get_url());
$form['#theme'] = views_theme_functions('views_exposed_form', $view, $display);
$form['#id'] = views_css_safe('views_exposed_form-' . check_plain($view->name) . '-' . check_plain($display->id));
// $form['#attributes']['class'] = array('views-exposed-form');
// If using AJAX, we need the form plugin.
if ($view->use_ajax) {
drupal_add_js('misc/jquery.form.js');
}
views_add_js('dependent');
return $form;
}
Or, you could use a preprocess function to alter the form even before it is build. I wanted to change the text on the button, so I did this:
function MYTHEME_preprocess_views_exposed_form(&$vars, $hook) {
// only alter the jobs search exposed filter form
if ($vars['form']['#id'] == 'views-exposed-form-jobs-search-page-1') {
// Change the text on the submit button
$vars['form']['submit']['#value'] = t('Search');
// Rebuild the rendered version (submit button, rest remains unchanged)
unset($vars['form']['submit']['#printed']);
$vars['button'] = drupal_render($vars['form']['submit']);
}
}
If you want the drop-down to fire, I'd use JavaScript instead of hacking the module as Eaton suggests.
Basically, you can modify the text with hook_form_alter as Eaton suggests, then use in the same hook_form_alter, add a call to drupal_add_js with your custom JS which hides the button and submits the form on the onChange handler of the select drop-down. You want that submit button there for those 10% of users for whom the JS fails.
Both of the above are fine but I found out that altering the form might not always lead to desirable results, mainly because exposed filters are themed using a specifc theme template. The proper way of changing the theme would be to override the views-exposed-form.tpl file in your theme's folder. Bear in mind that this will apply to all exposed filter forms, to theme a specific one, you will need to use a different name for that filename, like:
views-exposed-form--TITLE--DISPLAY.tpl.php
views-exposed-form--TITLE.tpl.php
and some others, you can check the Theme: Information section of your views for template naming conventions.
This module provides an auto-submit among other things http://drupal.org/project/views_hacks
This module is great to improving exposed filters http://drupal.org/project/better_exposed_filters
You should be able to use hook_form_alter() (http://api.drupal.org/api/function/hook_form_alter) to change the form as it's built, modifying the fields in question when that particular view is being displayed. You can nuke the submit button, add a #theme function that calls the drupal_add_js() function, and so on.
As long as the GET params come in the way views expect them, everything will work fine -- it was designed that way to allow bookmarking of pages with exposed filter settings, etc. The important part is to make sure you're doing the form mangling in your own module's hook_form_alter() function, so that it won't make other views driven stuff choke.

Resources