Create a selection of manually entered 'codes' in node edit form, avoiding redundancy

Events happening in the community are now at Drupal community events on www.drupal.org.
yan's picture

I was pointed here from the forum so I think it's ok to post my question again:


I am thinking about writing a small module for a quite special case and I would like to get some hints to point me in the right direction. The case is that I have a list of 'codes' that I need to add to a node in this way:

<img src="http://example.com/<CODE>" width="1" height="1" alt="" />

The code is unique for every node, but not every node necessarily has a code. Additionally, the list of new available codes must be downloaded every once in a while.

The perfect world: The list of up to 20 new codes is downloaded every once in a while and added to the database through a web interface. In the node edit form, the user has the chance to activate an option widget to add one of the available and unused codes. The code is then inserted into the node on the theme layer. Any ideas, how that could be accomplished?

Ideas I've had:

  • Add a simple text field and copy/paste the code there from a manually maintained list of codes.
  • Somehow feed a database table with the codes and offer them as a selection in the node edit form. Only codes that haven't been used before can be selected.

Views integration would be appreciated, too. That's why I have been thinking of a solution somehow connected to CCK.

Any suggestions?

Comments

CCK field or jQuery + small serverside

KNOFF's picture

I see two solutions to this task.
First - this is a separate field type for CCK. In this case the field will has simple text format, but its widget will be not so simple. First of all, you need to place checkbox with jQuery processing (on select, show other widget element). Other widget element is simple select with 20 options. User selecting one of them. And this code inserting to the db as field text value.
Second way is using simple text field but write little module for inserting js to add/edit form and provide ajax page with code list response.
In js file you must write simple jQuery conversion function that transform standard text field to checkbox and select with options from response to ajax page.
For me, second way is more easy than first. As I see, all ways using CCK and will integrating to views with no problems.

Thanks for your reply KNOFF.

yan's picture

Thanks for your reply KNOFF. It sounds interesting but I don't think I'll be able to realize this since I don't have js/jquery skills... But I'll see. You didn't include a way to add new codes through the admin, did you?

Some more brainstorming from my side:

1) Codes are entered into a text field in admin (one per line) and then written to a new database table like so:

+--------+-------+
|  code  |  nid  |
+--------+-------+
| ajshn2 |       |
| asdsaf |    4  |
| fghfgf |   23  |
| ergvdf |  276  |
| dfg5dv |       |
| g5ergd |    5  |
+--------+-------+

Where 'nid' means that a code has been assigned to a node.

2) In the edit form there is a simple on/off checkbox that says something like "add code". If that is checked, on node save a code from the table above that doesn't have an 'nid' set is selected and the new node's nid is inserted into the table.

3) The code is made available to $node for themeing purposes or (better) somehow included in a CCK field.

Combining this with what KNOFF said, maybe this could be realized through a new CCK field type that has a select list that is populated with codes that don't have a nid assigned yet.

Maybe I'm not quite correctly

KNOFF's picture

Maybe I'm not quite correctly understood your idea.
I think that we need for each newly created node, generate 20 codes, one of which the user chooses.
But it turns out it's much easier.
You can make this without any programming.
There are three ways:
1. If user can come up with its own code, you need add one text field for code and install unique_field module.
2. If you need your codes, use unique_field module and configure only one text field with select list widget and your codes in allowed values list.
3. If you need your codes, use unique_field module, content_taxonomy and taxonomy vocabulary for codes.
Format CCK field output as you like.
1. Really simple way, you get really unique codes and user did not see all code list.
2. Really simple but users will see all codes.
3. More hard way, users will see all codes, but you have more comfortable admin interface for codes manage.

In all cases you don't need write new module and they are compatible with views.

Thanks for your efforts,

yan's picture

Thanks for your efforts, KNOFF. Maybe I wasn't clear enough about the codes. The codes are offered by an institution external to my project. I need to include an image from one of their servers, including the code, to track visits to my nodes. To do so, I can download up to 20 codes at once from their website and then use them for 20 different nodes.

What you say about CCK fields and unique_field is interesting. Actually this afternoon I had some thoughts that point to the same direction. I was thinking that I could write a small module to easily enter the codes into a database table. Then that module also creates a CCK field that can be added to a node. That field could automatically be populated with a code that a) is in my module's table and b) is not in the CCK field's table (i.e., that is not in use so far). That way, the user wouldn't have to bother about selecting a code since that is done automatically.

Now that is one thing. But I want to have the possibility to select whether a node 'gets' a code or not. Maybe that could be done through a check box. Then, if the box is checked, the field's value (the code) is saved. If it is unchecked, the code is ignored.

That's just a rough draft, but maybe it's possible. Actually it is quite similar to what you wrote, I think.

My biggest problem is the lack of programming skills to realize that approach...

Okay, so the surest way - is

KNOFF's picture

Okay, so the surest way - is to develop your own CCK field. And probably not even need to use unique_field. It is necessary to base a standard text field and replace it's widget with checkbox.
Codes you can write to allowed values or separate table and on field validation stage you need to check availability of not used codes. (I recommend to use separate table and admin interface, but not with text area. You need a list table with current stored codes and nodes associated with codes, at the bottom of that table you can add text field for adding new codes, if you delete code from system, you need remove it's value from node field or delete associated node.)
Than simply place one code no field's value. Field will be saved correctly. If you write your own field, you can create exclusive display and present codes as you need. if you can't do it, I can make this module, but at now I have not much time, and in best case I can start only in the end of text month:(

Ok, so the concept would

yan's picture

Ok, so the concept would be:

Create a module that a) allows to enter codes and save them in a table and b) creates a CCK field that automatically provides a 'free' code. The widget would only be a checkbox that activates the code.

CCK is quite complex and I don't know much about it. If somebody could help me with that, I'd appreciate it a lot. There are two basic questions for me:

1) How can the field's value be automatically set from a database query?
2) How can I define the CCK field's widget to be just a checkbox?

Ok, I tried some things and

yan's picture

Ok, I tried some things and have some code that works more or less. But it's far from perfect and it's not all included in one module.

First, I wrote a small module called "Metis" (because the codes come from an institution in Germany, that calls them that way, it's the VG Wort). All the module does is provide a form to enter the codes and save them in the database. Here are the files:

metis.info

name = Metis
description = Include the METIS pixel for VG Wort counts
dependencies[] = content
dependencies[] = text
core = 6.x

metis.install

<?php
// $Id$

/**
* Implementation of hook_install().
*/

function metis_install() {
 
// Use schema API to create database table.
 
drupal_install_schema('metis');
}

/**
* Implementation of hook_uninstall().
*/

function metis_uninstall() {
 
// Use schema API to delete database table.
 
drupal_uninstall_schema('metis');
}

/**
* Implementation of hook_schema().
*/

function metis_schema() {
 
$schema = array();
 
$schema['metis'] = array(
   
'description' => t('Stores codes to use with the METIS pixel.'),
   
'fields' => array(
     
'code' => array(
       
'type' => 'varchar',
       
'length' => 128,
       
'not null' => TRUE,
       
'default' => ''
     
),
    ),
  );

  return
$schema;
}
?>

metis.module

<?php
function metis_menu() {

 
$items = array();

 
$items['admin/settings/metis'] = array(
   
'title' => 'Metis',
   
'description' => 'Add metis codes',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('metis_codes_form'),
   
'access arguments' => array('access administration pages'),
   
'type' => MENU_NORMAL_ITEM,
   );

  return
$items;
}

function
metis_codes_form_validate($form, &$form_state) {

 
$codes = trim($form_state['values']['code']);
 
$codes = explode("\n", $codes);
 
$codes = array_filter($codes, 'trim'); // remove any extra \r characters left behind

 
foreach ($codes as $code) {

    if (
strlen($code) != 32) form_set_error('metis_codes', t("%c is not 32 letters long", array('%c' => $code)));

  }

}

/**
* Define the form for entering a code
*/

function metis_codes_form() {

 
// Define a textarea
 
$form['code'] = array(
   
'#type' => 'textarea',
   
'#title' => t('Metis code'),
   
'#description' => t('Please enter Metis codes you want to save. <strong>One per line. Every code must be 32 letters long.</strong>'),
   
'#cols' => 32,
   
'#rows' => 20,
   
'#required' => TRUE,
  );

 
// Define a submit function.
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Save'),
  );

  return
$form;

}


/**
* Handle submission of the metis codes and saving
* of the data to the database.
*/

function metis_codes_form_submit($form, $form_state) {

 
$codes = trim($form_state['values']['code']);
 
$codes = explode("\n", $codes);
 
$codes = array_filter($codes, 'trim'); // remove any extra \r characters left behind
 
$codes_print = implode('<br />', $codes);

  foreach (
$codes as $code) {
   
db_query("INSERT INTO {metis} (code) VALUES ('%s')", str_replace("\r", '', $code));
  }

 
drupal_set_message(t("%n codes have been saved", array('%n' => count($codes))) . ':<br />' . $codes_print);

}
?>

Additionally, I defined a CCK text field with single on/off check box. There, I used the following code to define the allowed values:

<?php
$field
= 'metis_try'; // The machine readable name of the field
                      // How can I reference the field automatically?
$field_value = 'field_' . $field . '_value';

// Define an option for 'not checked'
$options = array('0' => t('No Metis Code'));

// Check if node already has a code set in this field on node edit
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'edit'){

 
$query = "SELECT field_%s_value
            FROM content_field_%s
            WHERE nid = %d"
;

 
$row = db_fetch_object(db_query($query, $field, $field, arg(1)));

  if (
count($row) && $row->$field_value != '0') {

   
// If code is set, use it and exit
   
$options[$row->$field_value] = t('Embed Metis code') . ' (' . $row->$field_value . ')';

    return
$options;

  }

}

// Select a new, unused code
if (arg(2) == 'edit' || !count($row) || $row->$field_value == 0){

 
$query = "SELECT metis.code
            FROM metis
            LEFT JOIN content_field_%s ON ( content_field_%s.field_%s_value = metis.code )
            WHERE content_field_%s.field_%s_value IS NULL
            LIMIT 1"
;

 
$row = db_fetch_object(db_query($query, $field, $field, $field, $field, $field));
 
$options[$row->code] = t('Embed Metis code') . ' (' . $row->code . ')';

}

return
$options;
?>

And for the default value:

<?php
return array(
 
0 => array('0' => '0')
);
?>

Now this works more or less, although I suppose the code could be way better. These two questions are still open:

1) How can I disable the checkbox once it has been selected? The problem is, that every code should only be used once, i.e. with one node. Once it has been used, it shouldn't be re-used in a different node. But when the checkbox gets unchecked again, the code counts as 'unused'. (See http://drupal.org/node/963998 (seems to be solved soon))

2) How do I provide the CCK field with the mentioned settings through my module? Now I have to set up the field manually.

I solved 1), but I'm still

yan's picture

I solved 1), but I'm still stuck with 2). Any ideas?

Contributed Module Ideas

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: