This issue came up as part of a larger feature request (http://drupal.org/node/187639) looking into supporting the large plethora of Tokens as arguments. That feature was a bit big for me but I did think I could find a way to at least get the Node Context for tokenized titles in URLs that were being placed in the URL Alias table by PathAuto. As it stands now, Panels only allows context to be gotten from the node ID and any site that has implemented tokens and PathAuto to create user friendly (most the time) URLs cannot have panels taking over node pages. And the Panel-node content type is not an option for sites that already have their content in specific content types.
I created a new plugin based heavily on merlinofchaos' nid.inc file but which when it detects that the argument is not a number will then check the URL Alias table for a match and if it finds it will retrieve the ID from there and load the node as usual. If it finds the Node ID already in the argument it will operate the exact same as if you had chose "Node ID" for an argument.
/**
* @file arguments/urlalias.inc
*
* Plugin to provide an argument handler for a node id or URL Alias
*/
function panels_urlalias_panels_arguments() {
$args['urlalias'] = array(
'title' => t("URL Alias"),
'keyword' => 'node', // keyword to use for %substitution
'description' => t('Restricts the argument to a node id found from a URL Alias or a node ID.'),
'context' => 'panels_urlalias_context',
'settings form' => 'panels_urlalias_settings_form',
'settings form submit' => 'panels_urlalias_settings_form_submit',
'displays' => 'panels_urlalias_displays',
'choose display' => 'panels_urlalias_choose_display',
);
return $args;
}
/**
* Discover if this argument gives us the node we crave.
*/
function panels_urlalias_context($arg = NULL, $conf = NULL, $empty = FALSE) {
// If unset it wants a generic, unfilled context.
if ($empty) {
return panels_context_create_empty('node');
}
if (!is_numeric($arg)) {
$nid = db_result(db_query("SELECT src FROM {url_alias} WHERE dst LIKE '%/%s'", $arg));
if($nid){
$nid = substr($nid, 5);
if (!ctype_digit($nid)) {
return FALSE;
}
}else{
return FALSE;
}
$arg = $nid;
}
$node = node_load($arg);
if (!$node) {
return FALSE;
}
if (array_filter($conf['types']) && empty($conf['types'][$node->type])) {
return FALSE;
}
return panels_context_create('node', $node);
}
/**
* Settings form for the argument
*/
function panels_urlalias_settings_form($conf) {
$options = array();
foreach (node_get_types() as $type => $info) {
$options[$type] = $info->name;
}
$form['types'] = array(
'#title' => t('Node types'),
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => $conf['types'],
'#prefix' => '<div class="clear-block">',
'#suffix' => '</div>',
);
$form['own_default'] = array(
'#title' => t('Use different default display'),
'#type' => 'checkbox',
'#description' => t('If checked, when this argument is present it will use its own display rather than the default. Displays not selected in the "Own display" field will use this one.'),
'#default_value' => $conf['own_default'],
);
$form['displays'] = array(
'#title' => t('Own display'),
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => $conf['displays'],
'#description' => t('Each checked type will get its own special display to layout its content. Only types set in Node types, above, should be set here. Types not set here will use the default display.'),
'#prefix' => '<div class="clear-block">',
'#suffix' => '</div>',
);
return $form;
}
/**
* There appears to be a bit of a bug with the way we're handling forms; it causes
* 'checkboxes' to get invalid values added to them when empty. This takes care
* of that.
*/
function panels_urlalias_settings_form_submit(&$values) {
$types = node_get_types();
if (!empty($values['types'])) {
foreach ($values['types'] as $type => $value) {
if (empty($types[$type])) {
unset($values['types'][$type]);
}
}
}
if (!empty($values['displays'])) {
foreach ($values['displays'] as $type => $value) {
if (empty($types[$type])) {
unset($values['displays'][$type]);
}
}
}
}
/**
* What additional displays does this argument provide?
*/
function panels_urlalias_displays($conf, $id) {
$displays = array();
if (!empty($conf['own_default'])) {
$displays['default'] = array(
'title' => t('Node ID @id Default', array('@id' => $id)),
'context' => 'node',
);
}
if (is_array($conf['displays'])) {
$options = array();
foreach (node_get_types() as $type => $info) {
$options[$type] = $info->name;
}
foreach (array_keys(array_filter($conf['displays'])) as $type) {
$displays[$type] = array(
'title' => t('Node ID @id @type', array('@id' => $id, '@type' => $options[$type])),
// Tell it to base the template for this display off of the default.
'default' => 'default',
'context' => 'node',
);
}
}
return $displays;
}
/**
* Based upon the settings and the context, choose which display to use.
*/
function panels_urlalias_choose_display($conf, $context) {
if (empty($context->data)) {
return;
}
if (!empty($conf['displays'][$context->data->type])) {
return $context->data->type;
}
// Please note that 'default' is a special display.
if (!empty($conf['own_default'])) {
return 'default';
}
// Empty return says to use the default display.
return;
}
-Jaks
Comments
Comment #1
smoothify commentedI think this is a very promising plugin, after a quick test I noticed something though
Currently it doesn't support matching top level paths. Say i have a node with a url path of example.com/title, this won't match your where clause in panels_urlalias_context.
This also doesn't seem to deal with handling multiple nodes that have a similar path,
e.g. say we are creating a music site and an artist has a self titled album there could be a conflict using the following paths
artists/artist-name
albums/album-title
For an artist like Stanley Clarke who made an album also called Stanley Clarke, the paths would be
artists/stanley-clarke
albums/stanley-clarke
Currently this plugin wouldn't be able to determine best way if i wanted to use this in my context. If I wanted to create a discography panel page - with the url:
artists/%/discography
there is a good chance that it will pick the album instead of the artist.
Sorry for the long winded explanations - i hope it makes some sense!
Comment #2
nath commentedSubscribing.
Comment #3
sdboyer commentedThis is indeed an interesting proposal - as you pointed out in that other original thread jshuell, for people who are concerned about having an intelligible URL system, panels seems to operate at cross-purposes. I've just come on as co-maintainer of Panels and have not yet plumbed the depths of merlin's menu voodoo, but what I do know leads me to believe that it may not be necessary to tackle URL aliasing at all in a context.
Drupal draws a clear distinction between its system paths and its aliased paths, and Panels is able to play with either on. It plays a lot nicer with system paths - that is, it plays nicer when you actually provide a system path in its argument (i.e., something like node/%) rather than providing an aliased path. However, if you do point the argument to the system path that aliases ultimately resolve to, Panels will still take over even if you point someone to the aliased path.
In other words, you ought to be able to do whatever you want with creating URL aliases, as long as you make sure you've got a panel page pointed at node/%. Please let me know if this explanation makes sense/takes care of your problem; if not, perhaps I've misunderstood the thrust of the code you've posted here and I can take another look at it.
Now, all that said, there is another problem that crops up even with the system paths: panels pages only allows one panel per URL, and since there's only one system path for viewing all nodes, that means you get ONE panel page for all your node types. Not very practical if you have a lot of different settings to create. There's a chance I may
Comment #4
Anonymous (not verified) commentedsubscribe. i need to review the comments and code more carefully, but i arrived here because my menus weren't being activated for nodes added directly to it when the nodes had panel page overrides.
Comment #5
toma commentedSubscribing
Comment #6
Anonymous (not verified) commentedThis looks great and I think it's what I need but can't seem to work out how to achieve.
I have a panel page - user profiles are located at users/username and I want my panel page url to be users/username/mysecondpage
I have a panel page set up with the url: users/%/mysecondpage and users/userid/mysecondpage works fine e.g. users/1/mysecondpage
Anyone any clues as to how I can do this?
Comment #7
sdboyer commentedJust a brief update on my thinking with this - IMO, the primary value of allowing path aliases was basically that it effectively circumvented the limitation of only being able to have one node/% panel page. However, switchers (the recently added Panels plugin type) handle that basic need considerably more elegantly, although they're likely to remain a very dark corner of panels until we can get some documentation together on them.
As I reflect more on the goal here, however, I'm increasingly leaning towards won't fixing this issue. I've come to notice recently that the distinction between system paths and path aliases is something that really confounds a lot of newer drupalers; even when the difference between the two is well enough understood conceptually, there's still a utilitarian knowledge that's required to really solidly grok which is which in application. For that reason alone, i think that introducing ambiguity as to whether Panels is operating using system paths or path aliases is a bad idea - it would serve to further the ambiguity between the two. But that's not the only reason. As it works right now, Panels (well, let's be clear, we're really talking about panels_page here) has no problem operating on the real system URL when a path alias is passed in. It's all done transparently, thanks to the way that the menu system and the path alias system interact.
Part of the problem here is that the OP is just incorrect on one point:
This is entirely untrue; context _can_ be gotten from MANY places in many ways. More importantly, though, it indicates that the very concerns I have about the differences between system paths and aliases are at work even right here - again, the aliased path is _irrelevant_ to panels_page, it picks up the node id regardless. With respect to the original issue re: tokens, that's best handled via some context creation, not reverse-engineering the path system.
In fact, I am gonna won't fix this. If there's a compelling argument to introduce this that I've missed, by all means please make it, but all I see right now is the potential for more ambiguity and confusion to accomplish something that Panels is already capable of.
If I understand the intention of the OP properly, then the end goal
Comment #8
jshuell commentedYou are correct in assuming I have a misunderstanding between the two path types and how panels works on them. I abandoned this after realizing my issue but I have yet to find a way to allow panels to override a node page. If I setup a panel page that has a path of node/% and use the argument to create the node context, then attempt to go to the node page either using node/1101 (or whatever) or news/dailynews I get the default node page as dressed up by Drupal and not the Panel page I created. I am somewhat new to Drupal but have been wrestling with this problem since day one as I need to allow my clients to control that node page and I do NOT want to have to use a tpl.php file. They are not coders.
When I originally posted this, I was under the impression that node/1101 would work, but that I was using pathauto and therefore panels wasnt seeing that path but rather the dressed up version of that path. My attempt in this thread was to introduce a look up function for panels to find the node/1101 path using the URL Alias. It, of course, did not work. But that does not mean that the functionality is not needed. We really need a way to force Drupal to send users to the panel page of a node instead of to the default node page. I have found hints online that some have been able to do it, but mostly I only find frustrated attempts, and like this thread, dead ends.
One of the most recent finds I had was here http://www.tejasa.com/node/160
As you can see it clearly lays out the path on how to let panels take over a group node. I tried this to no avail. So I am unsure if it is something in my setup/module mix that is causing the issue or if it is just plain not possible. Either way, this attempt, although good hearted, is definitely a dead end.
Comment #9
sdboyer commentedYes, it's something in your particular setup/mix of modules that's causing this problem. I've seen Panels override nodes using node/% across hundreds of different sites and setups. Probably the reason that you've only found hints as to how to do this is because Panels does provide this functionality out of the box, and it does so quite reliably.
As it regards path aliases and panels, though: now that I've written most of the menu system for panels_page in D6, I'm even more adamantly opposed to making Panels work using path aliases. _Nothing_ that can be done with path aliases can't be done in another better, smarter, more performant way. In many, probably most cases those ways will be more complex - but the solution is building UIs that simplifies them, not using path aliases. Allowing path aliases opens a can of worms that leads us towards really nasty performance, really bad design habits, more confusion about how drupal paths actually work, etc.; it would take some very serious, very compelling arguments that illustrate how pathauto is the one and ONLY way of doing something for me to reconsider.
Comment #10
summit commentedSubscribing, very interesting. Greetings, Martijn
Comment #11
artrow commentedI use panel2 and pathauto. I think your plugin is just what I want. However, I am not sure how to use it.
I have content type: story. The aliase URL is "story/[author-uid]/[yy][mm][dd]", for example: "node/81" has aliase URL "story/1/080310". I create a panel page, seeting its URL path as "mytest". I create a file named "urlalias.inc" and copy above plugin codes in it and save it in "arguments" subdirectory.
To use this panel page, it is OK if I use URL path: "mytest/81". But I don't know how to let this panel page work by using aliase URL. I try URL path "mytest/story/1/080310" or "mytest/1/080310" and fail.
Comment #12
ausvalue commentedI would love the comment
"_Nothing_ that can be done with path aliases can't be done in another better, smarter, more performant way."
to be explained. I would love to see examples to demonstrate this.
There are so many things that I want to do but can't figure out how to do it because Panels and the paths of Menu's don't allow the use of tokens.
Perhaps you could demonstrate how without doing path aliases you could achieve what implementers achieve with the type_local_nids module combined with path aliases using it.
Comment #13
freddyseubert commentedsubscribing too...
Comment #14
redben commentedsubscribing
Comment #15
AntiNSA commentedany updates for d6?