Problem

If you want to give a user the ability to update the values in a CCK text field select list, the only "built-in" way to do it is to give that user the very powerful and potentially dangerous "administer content types" permission.

Solution

An alternative, detailed here, is for the site developer to place PHP code into the "PHP Code" fieldset below the allowed values box on the textfield set up page in order to populate the list by parsing the text stored in a node.

At that point, the ability to edit the select list is controlled via node access. Users with access to a node with select-list options, referenced by the PHP code, will thus be able to control both the ordering and content of the select list without having "administer content-type" privileges.

For this example, assume that you have a content type of "resource" and a text field with a select widget called "author", such that a user creating new articles can pick from a select list of authors.

First, create a new node called "Author List", and list the authors you want in the select list in the body, with simple carriage returns between them. For example:

Jane Doe
John P. Smith
Jim Jones

(Note the node id of this node, you'll be referring to it in a minute.)

Now add the author field to the resource content type in admin/content/node-type/resource/fields. Remember that "resource" in that path is special to this example. You replace it with whatever the machine name is for the content-type containing the field. After setting the field type to "text" and the widget type to "select list", you'll get prompted to set the allowed values for the select list. Under the box titled "Allowed Values List" is a toggle for "PHP code", where you should add the following code snippet (here's where you use the node id):

Code

$node = node_load(27);//replace "27" with actual node id where the select options are
$body = strip_tags(str_replace(array("\r","\n"),";",$node->body));
$arr = explode(";",$body);
return drupal_map_assoc($arr);

What's happening here is that the body of the "Author List" node is being stripped of HTML tags, split into an array of strings, and then returned as an associative array. In this case, by using the drupal_map_assoc() function, the array keys are actually clones of the array values. This matches the functionality of the CCK module's allowed value list. This approach should work, whether or not a WYSIWYG is being used on the node body or what input filters are being applied.

Other Solutions

This isn't the only way to populate a select list without granting administer content types. Two other methods are:

  1. use taxonomy terms
  2. create the field as a node reference

Both of those methods have their own shortcomings.

Taxonomy permissions are not granular, and you would have to give the user in this example access to edit ALL taxonomy vocabularies. To mitigate that problem, one could use the Taxonomy Delegate. But maybe you don't want to and another module. In addition, the UI for administering taxonomy can be its own learning curve, whereas, for a content editor, creating a node is routine.

Creating authors as a node reference field would require a new content type ("author") and would force the user to add and maintain each author as a separate node.

Both of these alternatives are "heavier" than the one proposed using this snippet. The main advantage of those two methods over the one proposed here is that the items in the select list can be edited (i.e. their spellings changed) while still maintaining the same relationship with the content. In the method shown on this page, a spelling change in a list item requires previous nodes associated with that item to be edited again and saved with the new spelling (can be done using Views Bulk Operations module). This deficiency also exists when editing an allowed-values list on the content-type administration page.

Sample Warning Text

To mitigate against the problem of editing a select list item, here is a sample warning that you can use on the node/edit form on the node that contains the list items:

WARNING: If you change the spelling or title of an author (e.g. changing "Mr. John Doe" to "John Doe", it will NOT be retroactively applied to previously saved items. When displayed, items with the new spelling will appear listed separately as if they were written by a different author. If you must change the spelling, you must edit all previous items by the author with the changed spelling and select the author again and save.

Our approach seems to be a good compromise between the other approaches discussed here, combining both ease of maintenance and appropriate access control. Please let us know if you have any feedback on it.

Approach sparked by the following forum posts:

http://drupal.org/node/125464
http://drupal.org/node/492288

Comments

robcarr’s picture

Will work in Drupal 7 if you use the CCK module to allow PHP to be injected into 'Allowed values' field. Also nodes are now entities in D7 so loading nodes has a different API:

$nid = 1;//NID of node you are referencing
$delta = 0;
$language = 'und';

$entity = entity_load('node', array($nid));
$body = str_replace(array("\r","\n"),";",$entity[$nid]->body[$language][$delta]['safe_value']);
$arr = explode(";",$body);
return drupal_map_assoc($arr);
adamtong’s picture

i try this and it work in D7. But there is always a blank option as the last option, how to fix it?

Thank you.

shingy’s picture

Same for Drupal 8? Don't really want to upgrade if it doesn't work..

dizee’s picture

CCK says all features have been added to core, so it wasn't ported to D8, but I haven't been able to figure out how to do this in D8...
Has anyone created a select list based on PHP code?

I used to be able to create a select list in D7 and in "Allowed values PHP code" I had:

$j = json_decode(file_get_contents('feed url'));
$select_list = array();
foreach ($j as $n){
	$select_list[ID] = VAL;
}
return $select_list;