I have a simple view of fields (drupal 9) that lists nodes from a single content type (client). A node has a taxonomy reference field (field_client_country) where an editor can add the country or countries the client is from.
I need to extend the view so that nodes with, for example 'Italy', are brought to the top of the listing. After that, all the other nodes can be displayed.
How is this possible in views? I think i need to be in views_query_alter hook, but whatever i try it doesn't work.
function module_sticky_clients_views_query_alter(&$view, &$query)
{
$id = $view->id();
$country_ids = ['312', '112']; //IT-italy, then NL-netherlands
if ($id == 'view_clients') {
//bring nodes with $country_ids to top of listing, then show the rest
}
}
Related
In a Drupal 8 site, I have a taxonomy page with a view block on it. The view lists articles tagged with the term of the page you are on. It also shows articles tagged with the child terms of the current taxonomy page.
I am using the exposed filter "Content: Has taxonomy terms (with depth) (exposed)" to let a user filter the articles based on the child terms. Currently that filter shows all of the terms regardless of which taxonomy you are currently on.
Here is an example of the items listed in the exposed filter:
Mammals
- Cat
- Dog
Reptiles
- Lizard
- Snake
Amphibians
- Frog
- Salamander
The URL for one of the parent terms would be:
site.com/animal/mammals
I need to limit the list of options within the exposed filter to only show the children of the term based on the URL. So on the URL above, only Cat and Dog would be listed in the exposed filter.
In Drupal 7 I could accomplish this with a hook_form_alter in my theme.module using the URL arg(2) to get the term name. I cannot find any documentation on how to do this in Drupal 8.
Here is what I have found so far:
function myTheme_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id == 'views_exposed_form' && $form['#id'] == 'views-exposed-form-article-tags-block-1') {
$term = arg(2);
//Need D8 code to load term, find it's children and alter the select box to show those children
}
}
I am open to other options if this is not the way to accomplish my goal. Thank you in advance.
hook_form_alter will still work, though arg() is removed in Drupal 8.
It is not clear to me what the general replacement for arg() is. The code below uses two techniques to get the taxonomy term id.
You probably want to turn caching off in the view during development.
function myTheme_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id == 'views_exposed_form' && $form['#id'] == 'views-exposed-form-article-tags-block-1') {
// initial page load
$parameters = \Drupal::routeMatch()->getRawParameters();
$this_term_id = $parameters->get('taxonomy_term');
// ajax refresh via apply button
if (!isset($this_term_id)) {
$this_term_id = $_REQUEST['view_args'];
}
// get children of $this_term_id
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('tags', $parent = $this_term_id, $max_depth = NULL, $load_entities = FALSE);
// get rid of all options except default
$all_option = $form['term_node_tid_depth']['#options']['All'];
unset($form['term_node_tid_depth']['#options']);
$form['term_node_tid_depth']['#options']['All'] = $all_option;
// add child terms
foreach ($tree as $term) {
$option = new stdClass();
$option->option[$term->tid]=$term->name;
$form['term_node_tid_depth']['#options'][] = $option;
}
}
}
I am looking for some advice on how I might fetch an array with a list of links to node types the currently logged in user is allowed to create.
My client wants these links to populate a custom dropdown list which sits on the user profile page.
Just in case I don't manage to talk him out of it, I would like some technique/information to go on.
You will have to create a custom module. If you are creating your own module, this short snippet will give you an array ($types) with the links to content types the logged in user can create (D6). If the user cannot create any content types it will show a message:
<?php
$types = array();
foreach (node_get_types('types', NULL, TRUE) as $type) {
if (node_access('create', $type->type)) {
$types[$type->type] = l($type->name, 'node/add/' . str_replace('_', '-', $type->type));
}
}
if (count($types) == 0) {
drupal_set_message('You cannot create any content types!', 'warning');
}
?>
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
Using Drupal 6.x I have created two content types: Person and Event. Event has a custom field called Attendees (of type: Node Reference; unlimited number of values to person). When viewing a specific person how does one show all their events?
I have created a view (Personal Events) and added a block display. I enabled the block to show for content type Person. How should the view be defined? Or is there a better way?
Modules installed: CCK; Node Relationships; Views
I think one of these modules might be of help to you:
Reverse Node Reference
NodeReferrer
I have an answer to my own question. However, there maybe better answers... I can only hope.
Created content block (Personal Events)
Added this code to the body of the block. This code passes the node id argument to a view
<?php
if ( arg(0) == 'node' && is_numeric(arg(1)) && ! arg(2) ) {
$node = node_load(arg(1));
$args = array($node->nid );
$view = views_get_view('PersonalEvents');
print $view->preview('default', $args);
}
?>
Added this code to the Pages of the block [by selecting: Show if the following PHP code returns TRUE (PHP-mode, experts only)]... this drives the block to only appear person content.
<?php
//Read URL
$path=$_GET['q'];
//If URL is node page
if ( strpos($path,'node')===0){
//Parse URL to get nid
$links=explode("/",$_GET['q']);
$nid=$links[1];
//Load node
$node=node_load($nid);
//Display block only if node is of certain content type
if($node->type=='person'){
return TRUE;
}
}
return FALSE;
?>
Then created view with:
Style: Table
Relationship Content: Attendees (field_attendees); requires this relationship (checked); and Delta set to ALL.
Argument: Node: Nid; Relationship: Attendees; Hide view / Page not found (404) [selected]
Fields... simply selected Node Title and Date (for now)
Filter: Node Type = Event
Anyone have a better way?
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)