diff --git a/config_example/README.txt b/config_example/README.txt deleted file mode 100644 index 8627964..0000000 --- a/config_example/README.txt +++ /dev/null @@ -1,31 +0,0 @@ -Examples for Developers -http://drupal.org/project/examples - -This set of modules is intended to provide small working examples of Drupal's -features and APIs. The modules strive to be simple, well documented and -modification friendly to help developers quickly learn their inner workings, -with a little reading and hands on experience. - -You can have a quick glimpse at the current Examples in the demo site: -http://d7.drupalexamples.info/ - -Suggestions, corrections and new examples are welcome. Don't be shy if you're -just starting, as precisely because of that you will know the questions and -problems of a newcomer better than a seasoned developer! - -Setup - -1. Install Examples for Developers (unpacking it to your Drupal - sites/all/modules directory if you're isntalling by hand, for example). -2. Enable any Example modules in Admin menu > Site building > Modules. -3. Rebuild access permissions if you are prompted to. -4. Profit! The examples will appear in your Navigation menu (on the left - sidebar by default; you'll need to reenable it if you removed it). - -Now you can read the code and its comments and see the result, experiment with -it, and hopefully quickly grasp how things work. - -If you find a problem, incorrect comment, obsolete or improper code or such, -please let us know by creating a new issue at -http://drupal.org/project/issues/examples - diff --git a/config_example/config_example.info b/config_example/config_example.info deleted file mode 100644 index 6fb8d68..0000000 --- a/config_example/config_example.info +++ /dev/null @@ -1,12 +0,0 @@ -name = DBTNG example -description = An example module showing how use the database API: DBTNG. -package = Example modules -core = 7.x -files[] = dbtng_example.test - -; Information added by drupal.org packaging script on 2012-05-26 -version = "7.x-1.x-dev" -core = "7.x" -project = "examples" -datestamp = "1337991209" - diff --git a/config_example/config_example.install b/config_example/config_example.install deleted file mode 100644 index 9c7be8b..0000000 --- a/config_example/config_example.install +++ /dev/null @@ -1,117 +0,0 @@ - 'John', - 'surname' => 'Doe', - 'age' => 0, - ); - db_insert('dbtng_example') - ->fields($fields) - ->execute(); - - // Add another entry. - $fields = array( - 'name' => 'John', - 'surname' => 'Roe', - 'age' => 100, - 'uid' => 1, - ); - db_insert('dbtng_example') - ->fields($fields) - ->execute(); - -} - -/** - * Implements hook_uninstall(). - * - * As in hook_install, there is no need to uninstall schema, Drupal will do it - * for us. - * - * @see hook_uninstall() - * @ingroup dbtng_example - */ -function dbtng_example_uninstall() { - // nothing. -} - - -/** - * Implements hook_schema(). - * - * Defines the database tables used by this module. - * Remember that the easiest way to create the code for hook_schema is with - * the @link http://drupal.org/project/schema schema module @endlink - * - * @see hook_schema() - * @ingroup dbtng_example - */ -function dbtng_example_schema() { - - $schema['dbtng_example'] = array( - 'description' => 'Stores example person entries for demonstration purposes.', - 'fields' => array( - 'pid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique person ID.', - ), - 'uid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => "Creator user's {users}.uid", - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Name of the person.', - ), - 'surname' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Surname of the person.', - ), - 'age' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The age of the person in years.', - ) - ), - 'primary key' => array('pid'), - 'indexes' => array( - 'name' => array('name'), - 'surname' => array('surname'), - 'age' => array('age'), - ), - ); - - return $schema; -} diff --git a/config_example/config_example.module b/config_example/config_example.module deleted file mode 100644 index 92e7a2c..0000000 --- a/config_example/config_example.module +++ /dev/null @@ -1,570 +0,0 @@ -fields(array('name' => 'John', 'surname' => 'Doe')) - * ->execute(); - * @endcode - * - * db_update() example: - * @code - * // UPDATE {dbtng_example} SET name = 'Jane' WHERE name = 'John' - * db_update('dbtng_example') - * ->fields(array('name' => 'Jane')) - * ->condition('name', 'John') - * ->execute(); - * @endcode - * - * db_delete() example: - * @code - * // DELETE FROM {dbtng_example} WHERE name = 'Jane' - * db_delete('dbtng_example') - * ->condition('name', 'Jane') - * ->execute(); - * @endcode - * - * See @link database Database Abstraction Layer @endlink - * @see db_insert() - * @see db_update() - * @see db_delete() - * @see drupal_write_record() -*/ - -/** - * Save an entry in the database. - * - * The underlying DBTNG function is db_insert(). - * - * In Drupal 6, this would have been: - * @code - * db_query( - * "INSERT INTO {dbtng_example} (name, surname, age) - * VALUES ('%s', '%s', '%d')", - * $entry['name'], - * $entry['surname'], - * $entry['age'] - * ); - * @endcode - * - * Exception handling is shown in this example. It could be simplified - * without the try/catch blocks, but since an insert will throw an exception - * and terminate your application if the exception is not handled, it is best - * to employ try/catch. - * - * @param $entry - * An array containing all the fields of the database record. - * - * @see db_insert() - */ -function dbtng_example_entry_insert($entry) { - $return_value = NULL; - try { - $return_value = db_insert('dbtng_example') - ->fields($entry) - ->execute(); - } - catch (Exception $e) { - drupal_set_message(t('db_insert failed. Message = %message, query= %query', - array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error'); - } - return $return_value; -} - -/** - * Update an entry in the database. - * - * The former, deprecated techniques used db_query() or drupal_write_record(): - * @code - * drupal_write_record('dbtng_example', $entry, $entry['pid']); - * @endcode - * - * @code - * db_query( - * "UPDATE {dbtng_example} - * SET name = '%s', surname = '%s', age = '%d' - * WHERE pid = %d", - * $entry['pid'] - * ); - * @endcode - * - * @param $entry - * An array containing all the fields of the item to be updated. - * - * @see db_update() - */ -function dbtng_example_entry_update($entry) { - try { - // db_update()...->execute() returns the number of rows updated. - $count = db_update('dbtng_example') - ->fields($entry) - ->condition('pid', $entry['pid']) - ->execute(); - } - catch (Exception $e) { - drupal_set_message(t('db_update failed. Message = %message, query= %query', - array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error'); - } - return $count; -} - -/** - * Delete an entry from the database. - * - * The usage of db_query is deprecated except for static queries. - * Formerly, a deletion might have been accomplished like this: - * @code - * db_query("DELETE FROM {dbtng_example} WHERE pid = %d", $entry['pid]); - * @endcode - * - * @param $entry - * An array containing at least the person identifier 'pid' element of the - * entry to delete. - * - * @see db_delete() - */ -function dbtng_example_entry_delete($entry) { - db_delete('dbtng_example') - ->condition('pid', $entry['pid']) - ->execute(); - -} - - -/** - * Read from the database using a filter array. - * - * In Drupal 6, the standard function to perform reads was db_query(), and - * for static queries, it still is. - * - * db_query() used an SQL query with placeholders and arguments as parameters. - * - * @code - * // Old way - * $query = "SELECT * FROM {dbtng_example} n WHERE n.uid = %d AND name = '%s'"; - * $result = db_query($query, $uid, $name); - * @endcode - * - * Drupal 7 DBTNG provides an abstracted interface that will work with a wide - * variety of database engines. - * - * db_query() is deprecated except when doing a static query. The following is - * perfectly acceptable in Drupal 7. See - * @link http://drupal.org/node/310072 the handbook page on static queries @endlink - * - * @code - * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John' - * db_query( - * "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name", - * array(':uid' => 0, ':name' => 'John') - * )->execute(); - * @endcode - * - * But for more dynamic queries, Drupal provides the db_select() - * API method, so there are several ways to perform the same SQL query. - * See the @link http://drupal.org/node/310075 handbook page on dynamic queries. @endlink - * - * @code - * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John' - * db_select('dbtng_example') - * ->fields('dbtng_example') - * ->condition('uid', 0) - * ->condition('name', 'John') - * ->execute(); - * @endcode - * - * Here is db_select with named placeholders: - * @code - * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John' - * $arguments = array(':name' => 'John', ':uid' => 0); - * db_select('dbtng_example') - * ->fields('dbtng_example') - * ->where('uid = :uid AND name = :name', $arguments) - * ->execute(); - * @endcode - * - * Conditions are stacked and evaluated as AND and OR depending on the type of - * query. For more information, read the conditional queries handbook page at: - * http://drupal.org/node/310086 - * - * The condition argument is an 'equal' evaluation by default, but this can be - * altered: - * @code - * // SELECT * FROM {dbtng_example} WHERE age > 18 - * db_select('dbtng_example') - * ->fields('dbtng_example') - * ->condition('age', 18, '>') - * ->execute(); - * @endcode - * - * @param $entry - * An array containing all the fields used to search the entries in the table. - * @return - * An object containing the loaded entries if found. - * - * @see db_select() - * @see db_query() - * @see http://drupal.org/node/310072 - * @see http://drupal.org/node/310075 - * - */ -function dbtng_example_entry_load($entry = array()) { - // Read all fields from the dbtng_example table. - $select = db_select('dbtng_example', 'example'); - $select->fields('example'); - - // Add each field and value as a condition to this query. - foreach ($entry as $field => $value) { - $select->condition($field, $value); - } - // Return the result in object format. - return $select->execute()->fetchAll(); -} - -/** - * Render a filtered list of entries in the database. - * - * DBTNG also helps processing queries that return several rows, providing the - * found objects in the same query execution call. - * - * This function queries the database using a JOIN between users table and the - * example entries, to provide the username that created the entry, and creates - * a table with the results, processing each row. - * - * SELECT - * e.pid as pid, e.name as name, e.surname as surname, e.age as age - * u.name as username - * FROM - * {dbtng_example} e - * JOIN - * users u ON e.uid = u.uid - * WHERE - * e.name = 'John' AND e.age > 18 - * - * @see db_select() - * @see http://drupal.org/node/310075 - */ -function dbtng_example_advanced_list() { - $output = ''; - - $select = db_select('dbtng_example', 'e'); - // Join the users table, so we can get the entry creator's username. - $select->join('users', 'u', 'e.uid = u.uid'); - // Select these specific fields for the output. - $select->addField('e', 'pid'); - $select->addField('u', 'name', 'username'); - $select->addField('e', 'name'); - $select->addField('e', 'surname'); - $select->addField('e', 'age'); - // Filter only persons named "John". - $select->condition('e.name', 'John'); - // Filter only persons older than 18 years. - $select->condition('e.age', 18, '>'); - // Make sure we only get items 0-49, for scalability reasons. - $select->range(0, 50); - - // Now, loop all these entries and show them in a table. Note that there is no - // db_fetch_* object or array function being called here. Also note that the - // following line could have been written as - // $entries = $select->execute()->fetchAll() which would return each selected - // record as an object instead of an array. - $entries = $select->execute()->fetchAll(PDO::FETCH_ASSOC); - if (!empty($entries)) { - $rows = array(); - foreach ($entries as $entry) { - // Sanitize the data before handing it off to the theme layer. - $rows[] = array_map('check_plain', $entry); - } - // Make a table for them. - $header = array(t('Id'), t('Created by'), t('Name'), t('Surname'), t('Age')); - $output .= theme('table', array('header' => $header, 'rows' => $rows)); - } - else { - drupal_set_message(t('No entries meet the filter criteria (Name = "John" and Age > 18).')); - } - return $output; -} - -//// Helper functions //// - -/** - * Implements hook_help(). - * - * Show some help on each form provided by this module. - */ -function dbtng_example_help($path) { - $output = ''; - switch ($path) { - case 'examples/dbtng': - $output = t('Generate a list of all entries in the database. There is no filter in the query.'); - break; - case 'examples/dbtng/advanced': - $output = t('A more complex list of entries in the database.') . ' '; - $output .= t('Only the entries with name = "John" and age older than 18 years are shown, the username of the person who created the entry is also shown.'); - break; - case 'examples/dbtng/update': - $output = t('Demonstrates a database update operation.'); - break; - case 'examples/dbtng/add': - $output = t('Add an entry to the dbtng_example table.'); - break; - } - return $output; -} - -/** - * Implements hook_menu(). - * - * Set up calls to drupal_get_form() for all our example cases. - */ -function dbtng_example_menu() { - $items = array(); - - $items['examples/dbtng'] = array( - 'title' => 'DBTNG Example', - 'page callback' => 'dbtng_example_list', - 'access callback' => TRUE, - ); - $items['examples/dbtng/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['examples/dbtng/add'] = array( - 'title' => 'Add entry', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('dbtng_example_form_add'), - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => -9, - ); - $items['examples/dbtng/update'] = array( - 'title' => 'Update entry', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('dbtng_example_form_update'), - 'type' => MENU_LOCAL_TASK, - 'access callback' => TRUE, - 'weight' => -5, - ); - $items['examples/dbtng/advanced'] = array( - 'title' => 'Advanced list', - 'page callback' => 'dbtng_example_advanced_list', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -/** - * Render a list of entries in the database. - */ -function dbtng_example_list() { - $output = ''; - - // Get all entries in the dbtng_example table. - if ($entries = dbtng_example_entry_load()) { - $rows = array(); - foreach ($entries as $entry) { - // Sanitize the data before handing it off to the theme layer. - $rows[] = array_map('check_plain', (array) $entry); - } - // Make a table for them. - $header = array(t('Id'), t('uid'), t('Name'), t('Surname'), t('Age')); - $output .= theme('table', array('header' => $header, 'rows' => $rows)); - } - else { - drupal_set_message(t('No entries have been added yet.')); - } - return $output; -} - -/** - * Prepare a simple form to add an entry, with all the interesting fields. - */ -function dbtng_example_form_add($form, &$form_state) { - $form = array(); - - $form['add'] = array( - '#type' => 'fieldset', - '#title' => t('Add a person entry'), - ); - $form['add']['name'] = array( - '#type' => 'textfield', - '#title' => t('Name'), - '#size' => 15, - ); - $form['add']['surname'] = array( - '#type' => 'textfield', - '#title' => t('Surname'), - '#size' => 15, - ); - $form['add']['age'] = array( - '#type' => 'textfield', - '#title' => t('Age'), - '#size' => 5, - '#description' => t("Values greater than 127 will cause an exception. Try it - it's a great example why exception handling is needed with DTBNG."), - ); - $form['add']['submit'] = array( - '#type' => 'submit', - '#value' => t('Add'), - ); - - return $form; -} - -/** - * Submit handler for 'add entry' form. - */ -function dbtng_example_form_add_submit($form, &$form_state) { - global $user; - - // Save the submitted entry. - $entry = array( - 'name' => $form_state['values']['name'], - 'surname' => $form_state['values']['surname'], - 'age' => $form_state['values']['age'], - 'uid' => $user->uid, - ); - $return = dbtng_example_entry_insert($entry); - if ($return) { - drupal_set_message(t("Created entry @entry", array('@entry' => print_r($entry, TRUE)))); - } -} - -/** - * Sample UI to update a record. - */ -function dbtng_example_form_update($form, &$form_state) { - $form = array( - '#prefix' => '
', - '#suffix' => '
', - ); - - $entries = dbtng_example_entry_load(); - $keyed_entries = array(); - if (empty($entries)) { - $form['no_values'] = array( - '#value' => t("No entries exist in the table dbtng_example table."), - ); - return $form; - } - - foreach ($entries as $entry) { - $options[$entry->pid] = t("@pid: @name @surname (@age)", array('@pid' => $entry->pid, '@name' => $entry->name, '@surname' => $entry->surname, '@age' => $entry->age)); - $keyed_entries[$entry->pid] = $entry; - } - $default_entry = !empty($form_state['values']['pid']) ? $keyed_entries[$form_state['values']['pid']] : $entries[0]; - - $form_state['entries'] = $keyed_entries; - - $form['pid'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('Choose entry to update'), - '#default_value' => $default_entry->pid, - '#ajax' => array( - 'wrapper' => 'updateform', - 'callback' => 'dbtng_example_form_update_callback', - ), - ); - - $form['name'] = array( - '#type' => 'textfield', - '#title' => t('Updated first name'), - '#size' => 15, - '#default_value' => $default_entry->name, - ); - - $form['surname'] = array( - '#type' => 'textfield', - '#title' => t('Updated last name'), - '#size' => 15, - '#default_value' => $default_entry->surname, - ); - $form['age'] = array( - '#type' => 'textfield', - '#title' => t('Updated age'), - '#size' => 4, - '#default_value' => $default_entry->age, - '#description' => t("Values greater than 127 will cause an exception"), - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Update'), - ); - return $form; -} - -/** - * AJAX callback handler for the pid select. - * - * When the pid changes, populates the defaults from the database in the form. - */ -function dbtng_example_form_update_callback($form, $form_state) { - $entry = $form_state['entries'][$form_state['values']['pid']]; - // Setting the #value of items is the only way I was able to figure out - // to get replaced defaults on these items. #default_value will not do it - // and shouldn't. - foreach (array('name', 'surname', 'age') as $item) { - $form[$item]['#value'] = $entry->$item; - } - return $form; -} - -/** - * Submit handler for 'update entry' form. - */ -function dbtng_example_form_update_submit($form, &$form_state) { - global $user; - - // Save the submitted entry. - $entry = array( - 'pid' => $form_state['values']['pid'], - 'name' => $form_state['values']['name'], - 'surname' => $form_state['values']['surname'], - 'age' => $form_state['values']['age'], - 'uid' => $user->uid, - ); - $count = dbtng_example_entry_update($entry); - drupal_set_message(t("Updated entry @entry (@count row updated)", array('@count' => $count, '@entry' => print_r($entry, TRUE)))); -} -/** - * @} End of "defgroup dbtng_example". - */ diff --git a/config_example/config_example.test b/config_example/config_example.test deleted file mode 100644 index 2f7ae56..0000000 --- a/config_example/config_example.test +++ /dev/null @@ -1,179 +0,0 @@ - 'DBTNG example unit and UI tests', - 'description' => 'Various unit tests on the dbtng example module.' , - 'group' => 'Examples', - ); - } - - function setUp() { - parent::setUp('dbtng_example'); - } - - /** - * Test default module installation, two entries in the database table. - */ - function testInstall() { - $result = dbtng_example_entry_load(); - $this->assertEqual( - count($result), - 2, - t('Found two entries in the table after installing the module.') - ); - } - - - /** - * Test the UI. - */ - function testUI() { - // Test the basic list. - $this->drupalGet('examples/dbtng'); - $this->assertPattern("/John[td\/<>\w]+Doe/", t("Text 'John Doe' found in table")); - - //Test the add tab. - // Add the new entry. - $this->drupalPost('examples/dbtng/add', - array('name' => 'Some', 'surname' => 'Anonymous', 'age' => 33), t('Add')); - // Now find the new entry. - $this->drupalGet('examples/dbtng'); - $this->assertPattern("/Some[td\/<>\w]+Anonymous/", t("Text 'Some Anonymous' found in table")); - - - // Try the update tab. - // Find out the pid of our "anonymous" guy. - $result = dbtng_example_entry_load(array('surname' => 'Anonymous')); - $this->drupalGet("examples/dbtng"); - $this->assertEqual( - count($result), - 1, - t('Found one entry in the table with surname = "Anonymous".') - ); - $entry = $result[0]; - unset($entry->uid); - $entry->name = 'NewFirstName'; - $this->drupalPost('examples/dbtng/update', - (array)$entry, t('Update')); - // Now find the new entry. - $this->drupalGet('examples/dbtng'); - $this->assertPattern("/NewFirstName[td\/<>\w]+Anonymous/", t("Text 'NewFirstName Anonymous' found in table")); - - // Try the advanced tab. - $this->drupalGet('examples/dbtng/advanced'); - $rows = $this->xpath("//*[@id='block-system-main']/div/table[1]/tbody/tr"); - $this->assertEqual(count($rows), 1, t("One row found in advanced view")); - $this->assertFieldByXPath("//*[@id='block-system-main']/div/table[1]/tbody/tr/td[4]", "Roe", "Name 'Roe' Exists in advanced list"); - } - - /** - * Test several combinations, adding entries, updating and deleting. - */ - function testAPIExamples() { - // Create a new entry. - $entry = array( - 'name' => 'James', - 'surname' => 'Doe', - 'age' => 23, - ); - dbtng_example_entry_insert($entry); - - // Save another entry - $entry = array( - 'name' => 'Jane', - 'surname' => 'NotDoe', - 'age' => 19, - ); - dbtng_example_entry_insert($entry); - - // Verify that 4 records are found in the database - $result = dbtng_example_entry_load(); - $this->assertEqual( - count($result), - 4, - t('Found a total of four entries in the table after creating two additional entries.') - ); - - // Verify 2 of these records have 'Doe' as surname - $result = dbtng_example_entry_load(array('surname' => 'Doe')); - $this->assertEqual( - count($result), - 2, - t('Found two entries in the table with surname = "Doe".') - ); - - // Now find our not-Doe entry. - $result = dbtng_example_entry_load(array('surname' => 'NotDoe')); - $this->assertEqual( - count($result), - 1, - t('Found one entry in the table with surname "NotDoe')); - // Our NotDoe will be changed to "NowDoe". - $entry = $result[0]; - $entry->surname = "NowDoe"; - dbtng_example_entry_update((array)$entry); - - $result = dbtng_example_entry_load(array('surname' => 'NowDoe')); - $this->assertEqual( - count($result), - 1, - t("Found renamed 'NowDoe' surname")); - - // Read only John Doe entry. - $result = dbtng_example_entry_load(array('name' => 'John', 'surname' => 'Doe')); - $this->assertEqual( - count($result), - 1, - t('Found one entry for John Doe.') - ); - // Get the entry - $entry = (array) end($result); - // Change age to 45 - $entry['age'] = 45; - // Update entry in database - dbtng_example_entry_update((array)$entry); - - // Find entries with age = 45 - // Read only John Doe entry. - $result = dbtng_example_entry_load(array('surname' => 'NowDoe')); - $this->assertEqual( - count($result), - 1, - t('Found one entry with surname = Nowdoe.') - ); - - // Verify it is Jane NowDoe. - $entry = (array) end($result); - $this->assertEqual( - $entry['name'], - 'Jane', - t('The name Jane is found in the entry') - ); - $this->assertEqual( - $entry['surname'], - 'NowDoe', - t('The surname NowDoe is found in the entry') - ); - - // Delete the entry. - dbtng_example_entry_delete($entry); - - // Verify that now there are only 3 records - $result = dbtng_example_entry_load(); - $this->assertEqual( - count($result), - 3, - t('Found only three records, a record was deleted.') - ); - } -} diff --git a/configuration_example/README.txt b/configuration_example/README.txt new file mode 100644 index 0000000..1c24ec5 --- /dev/null +++ b/configuration_example/README.txt @@ -0,0 +1,13 @@ +Configuration Example +Part of Examples for developers +(http://drupal.org/project/examples) + +This module demonstrates how to set up configuration for a module. + +Note the difference between configuration and content. Content types, Image Styles, Taxonomy vocabularies are configuration. Nodes, Images, and Taxonomy terms are content. This module shows you how to manage configuration, not content, in your module. + +Two configuration types are demonstrated: + +(1) simple configuration: site-wide configuration variables. Our example uses "Configuration example variable one" and "Configuration example variable two". + +(2) complex configuration: variable number of configuration sets. Our example lets you define as many "kittens" as you want, and for each kitten, define a color and size. \ No newline at end of file diff --git a/configuration_example/configuration_example.info b/configuration_example/configuration_example.info new file mode 100644 index 0000000..1221551 --- /dev/null +++ b/configuration_example/configuration_example.info @@ -0,0 +1,5 @@ +name = Configuration example +description = An example module showing how to set up configurations for your module. +package = Example modules +core = 7.x +files[] = configuration_example.test diff --git a/configuration_example/configuration_example.install b/configuration_example/configuration_example.install new file mode 100644 index 0000000..a0b97f3 --- /dev/null +++ b/configuration_example/configuration_example.install @@ -0,0 +1,115 @@ + 'big_white_kitten', + 'name' => 'Big white kitten', + 'color' => 'white', + 'size' => 'big', + ); + db_insert('configuration_example') + ->fields($fields) + ->execute(); + + // Add a few entries. + $fields = array( + 'kid' => 'small_yellow_kitten', + 'name' => 'Small yellow kitten', + 'color' => 'small', + 'size' => 'yellow', + ); + db_insert('configuration_example') + ->fields($fields) + ->execute(); +} + +/** + * Implements hook_uninstall(). + * + * As in hook_install, there is no need to uninstall schema, Drupal will do it + * for us. We will simply delete our variable. + * + * @see hook_uninstall() + * @ingroup configuration_example + */ +function configuration_example_uninstall() { + // Delete any variables that might have been set by the module. + variable_del('configuration_example_one'); + variable_del('configuration_example_two'); +} + + +/** + * Implements hook_schema(). + * + * Defines the database tables used by this module. + * Remember that the easiest way to create the code for hook_schema is with + * the @link http://drupal.org/project/schema schema module @endlink + * + * @see hook_schema() + * @ingroup configuration_example + */ +function configuration_example_schema() { + + $schema['configuration_example'] = array( + 'description' => 'Stores example kitten entries for demonstration purposes.', + 'fields' => array( + // kid stands for "kitten id". If you are storing something else, + // like beach balls, you might call your primary field bbid. + 'kid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Primary Key: Unique kitten ID (machine name).', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Kitten\'s name.', + ), + 'color' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Kitten\'s color.', + ), + 'size' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Kitten\'s solor.', + ), + ), + 'primary key' => array('kid'), + 'indexes' => array( + 'color' => array('color'), + 'size' => array('size'), + ), + ); + + return $schema; +} diff --git a/configuration_example/configuration_example.module b/configuration_example/configuration_example.module new file mode 100644 index 0000000..71992a7 --- /dev/null +++ b/configuration_example/configuration_example.module @@ -0,0 +1,397 @@ +fields($entry) + ->execute(); + } + catch (Exception $e) { + drupal_set_message(t('db_insert failed. Message = %message, query= %query', + array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error'); + } + return $return_value; +} + +/** + * Update an entry in the database. + * + * @param $entry + * An array containing all the fields of the item to be updated. + * + * @see db_update() + */ +function configuration_example_entry_update($entry) { + try { + // db_update()...->execute() returns the number of rows updated. + $count = db_update('configuration_example') + ->fields($entry) + ->condition('kid', $entry['kid']) + ->execute(); + } + catch (Exception $e) { + drupal_set_message(t('db_update failed. Message = %message, query= %query', + array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error'); + } + return $count; +} + +/** + * Delete an entry from the database. + * + * @param $entry + * An array containing at least the kitten identifier 'kid' element of the + * entry to delete. + * + * @see db_delete() + */ +function configuration_example_form_delete($form, &$form_state) { + $kid = arg(2); + if (!configuration_example_kid_exists($kid)) { + drupal_set_message(t('The kitten @kid does not exist and cannot be deleted.', array('@kid' => check_plain($kid)))); + drupal_goto('examples/configuration-complex/list'); + } + + $form['kid'] = array( + '#type' => 'hidden', + '#value' => $kid, + ); + + return confirm_form( + $form, + t('Are you sure you want to delete kitten @kid?', + array('@kid' => $kid)), + 'examples/configuration-complex' + ); +} + +function configuration_example_form_delete_submit($form, &$form_state) { + db_delete('configuration_example') + ->condition('kid', $form['kid']['#value']) + ->execute(); +} + +/** + * Read all entries from the database. + * + * @param $kid + * a machine id of a kitten to load. Ignored if the kid is + * invalid or does not exist + * + * @return + * An object containing the loaded entries if found. + */ +function configuration_example_entry_load($kid = NULL) { + // Read all fields from the configuration_example table. + $select = db_select('configuration_example', 'example'); + $select->fields('example'); + if (configuration_example_kid_exists($kid)) { + $select->condition('kid', $kid); + } + + // Return the result in object format. + return $select->execute()->fetchAll(); +} + +/** + * Implements hook_help(). + * + * Show some help on each form provided by this module. + */ +function configuration_example_help($path) { + $output = ''; + switch ($path) { + case 'examples/configuration-simple': + $output = t('Demonstrates how to manage simple site-wide configuration options.'); + break; + case 'examples/configuration-complex': + $output = t('Demonstrates how to manage complex groups of configuration data for your site.'); + break; + case 'examples/configuration-complex/%/delete': + $output = t('Demonstrates how to delete a configuration group.'); + break; + case 'examples/configuration-complex/%/edit': + $output = t('Demonstrates how to edit a configuration group.'); + break; + case 'examples/configuration-complex/add': + $output = t('Demonstrates how to add a configuration group.'); + break; + } + return $output; +} + +/** + * Implements hook_menu(). + * + * Set up calls to drupal_get_form() for all our example cases. + */ +function configuration_example_menu() { + $items = array(); + + $items['examples/configuration-simple'] = array( + 'title' => 'Configuration example - Simple', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('configuration_example_form_simple'), + 'access callback' => TRUE, + ); + $items['examples/configuration-complex'] = array( + 'title' => 'Configuration example - Complex', + 'page callback' => 'configuration_example_complex_list', + 'access callback' => TRUE, + ); + $items['examples/configuration-complex/list'] = array( + 'title' => 'Complex configuration list', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items['examples/configuration-complex/add'] = array( + 'title' => 'Add a complex configuration item', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('configuration_example_form_add'), + 'access callback' => TRUE, + 'type' => MENU_LOCAL_TASK, + ); + $items['examples/configuration-complex/%/update'] = array( + 'title' => 'Update (edit) a complex configuration item', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('configuration_example_form_update'), + 'access callback' => TRUE, + ); + $items['examples/configuration-complex/%/delete'] = array( + 'title' => 'Delete entry', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('configuration_example_form_delete'), + 'access callback' => TRUE, + ); + + return $items; +} + +/** + * Render a list of entries in the database. + */ +function configuration_example_complex_list() { + $output = ''; + + // Get all entries in the configuration_example table. + if ($entries = configuration_example_entry_load()) { + $rows = array(); + foreach ($entries as $entry) { + // Sanitize the data before handing it off to the theme layer. + $row = array_map('check_plain', (array) $entry); + $row[] = l(t('edit'), 'examples/configuration-complex/' . $row['kid'] . '/update'); + $row[] = l(t('delete'), 'examples/configuration-complex/' . $row['kid'] . '/delete'); + $rows[] = $row; + } + // Make a table for them. + $header = array(t('Machine name'), t('Name'), t('size'), t('color'), t('edit'), t('delete')); + $output .= theme('table', array('header' => $header, 'rows' => $rows)); + } + else { + drupal_set_message(t('No kittens are defined.')); + } + return $output; +} + +/** + * Prepare a simple form to add an entry, with all the interesting fields. + */ +function configuration_example_form_add($form, &$form_state) { + $form = array(); + + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Kitten name'), + '#required' => TRUE, + '#description' => t('User-friendly name.'), + '#size' => 40, + '#maxlength' => 127, + '#default_value' => '', + ); + $form['kid'] = array( + '#type' => 'machine_name', + '#required' => TRUE, + '#title' => t("Machine Name"), + '#required' => TRUE, + '#description' => t('machine-friendly name.'), + '#size' => 15, + '#maxlength' => 15, + '#default_value' => '', + '#machine_name' => array( + 'exists' => 'configuration_example_kid_exists', + 'source' => array('name'), + 'label' => t('Machine nmae'), + 'replace_pattern' => '[^a-z0-9-]+', + 'replace' => '-', + ), + ); + $form['color'] = array( + '#type' => 'textfield', + '#required' => TRUE, + '#title' => t('Color'), + '#size' => 15, + ); + $form['size'] = array( + '#type' => 'textfield', + '#required' => TRUE, + '#title' => t('Size'), + '#size' => 15, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Add'), + ); + + return $form; +} + +/** + * Submit handler for 'add entry' form. + */ +function configuration_example_form_add_submit($form, &$form_state) { + // Save the submitted entry. + $entry = array( + 'kid' => $form_state['values']['kid'], + 'name' => $form_state['values']['name'], + 'size' => $form_state['values']['size'], + 'color' => $form_state['values']['color'], + ); + $return = configuration_example_entry_insert($entry); + if ($return) { + drupal_set_message(t("Created entry @entry", array('@entry' => print_r($entry, TRUE)))); + } + + $form_state['redirect'] = 'examples/configuration-complex'; + + return $return; +} + +/** + * Sample UI to update a record. + */ +function configuration_example_form_update($form, &$form_state) { + $kid = arg(2); + if (!configuration_example_kid_exists($kid)) { + drupal_set_message(t('The kitten @kid does not exist and cannot be updated.', array('@kid' => check_plain($kid)))); + drupal_goto('examples/configuration-complex/list'); + } + + $entries = configuration_example_entry_load($kid); + + $form['kid'] = array( + '#type' => 'hidden', + '#value' => $kid, + ); + + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Name'), + '#size' => 15, + '#default_value' => $entries[0]->name, + ); + + $form['color'] = array( + '#type' => 'textfield', + '#title' => t('Color'), + '#size' => 15, + '#default_value' => $entries[0]->color, + ); + $form['size'] = array( + '#type' => 'textfield', + '#title' => t('Size'), + '#size' => 15, + '#default_value' => $entries[0]->size, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Update'), + ); + return $form; +} + +/** + * Submit handler for 'update entry' form. + */ +function configuration_example_form_update_submit($form, &$form_state) { + // Save the submitted entry. + $entry = array( + 'kid' => $form_state['values']['kid'], + 'name' => $form_state['values']['name'], + 'color' => $form_state['values']['color'], + 'size' => $form_state['values']['size'], + ); + $count = configuration_example_entry_update($entry); + drupal_set_message(t("Updated entry @entry (@count row updated)", array('@count' => $count, '@entry' => print_r($entry, TRUE)))); + + $form_state['redirect'] = 'examples/configuration-complex'; +} + +/** + * Returns whether a kitten machine name already exists. + * + * @param @kid + * A kitten ID. + */ +function configuration_example_kid_exists($kid) { + return db_query_range('SELECT 1 FROM {configuration_example} WHERE kid = :kid', 0, 1, array(':kid' => $kid))->fetchField(); +} + +/** + * Sample UI to update a simple configuration item. + */ +function configuration_example_form_simple($form, &$form_state) { + $form['configuration_example_one'] = array( + '#type' => 'textfield', + '#title' => t('Configuration example variable one'), + '#size' => 15, + '#default_value' => variable_get('configuration_example_one', 'Default value one'), + ); + + $form['configuration_example_two'] = array( + '#type' => 'textfield', + '#title' => t('Configuration example variable two'), + '#size' => 15, + '#default_value' => variable_get('configuration_example_two', 'Default value two'), + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Update'), + ); + + return $form; +} + +/** + * Submit handler for 'configuration_example_form_update' form. + * + * @TODO this works, but there also is a way (which I would not recommend) + * to get the FAPI to automatically store variables in the variables table. + * I can't get it to work so I'm leaving it at this. + */ +function configuration_example_form_simple_submit($form, &$form_state) { + // you can do what you want with the variables. Let's store them + // to the database's variable table. + variable_set('configuration_example_one', $form_state['input']['configuration_example_one']); + variable_set('configuration_example_two', $form_state['input']['configuration_example_two']); + drupal_set_message(t('Successfully updated the simple configuration variables')); +} diff --git a/configuration_example/configuration_example.test b/configuration_example/configuration_example.test new file mode 100644 index 0000000..a4da603 --- /dev/null +++ b/configuration_example/configuration_example.test @@ -0,0 +1,63 @@ + 'Configuration example UI tests', + 'description' => 'Various unit tests on the configuration example module.' , + 'group' => 'Examples', + ); + } + + function setUp() { + parent::setUp('configuration_example'); + } + + function testUI() { + // simple config + $this->drupalGet('examples/configuration-simple'); + $this->assertRaw('Default value one', '"Default value one", provided by default, is present.'); + $this->drupalPost(NULL, array('configuration_example_one' => 'Updated value one'), t('Update')); + $this->assertNoRaw('Default value one', '"Default value one" has been updated and is no longer present.'); + $this->assertRaw('Updated value one', '"Updated value one", entered by the user, is present.'); + + // complex config + $this->drupalGet('examples/configuration-complex'); + $this->assertText('Big white kitten', 'The kitten "Big white kitten", installed by default, is present after installation'); + $new_name = $this->randomName(8); + $new_kid = strtolower($this->randomName(8); + $new_color = $this->randomName(8); + $new_size = $this->randomName(8); + $edit = array( + 'name' => $new_name, + 'kid' => $new_kid, + 'color' => $new_color, + 'size' => $new_size, + ); + $this->drupalPost('examples/configuration-complex/add', $edit, t('Add')); + $this->assertText($new_name, 'The name of the new kitten is present'); + $this->assertText($new_kid, 'The machine name of the new kitten is present'); + $this->assertText($new_color, 'The color of the new kitten is present'); + $this->assertText($new_size, 'The size of the new kitten is present'); + + $new_color = $this->randomString(8); + $this->drupalPost('examples/configuration-complex/big_white_kitten/update', array('color' => $new_color), t('Update')); + $this->assertText($new_color, 'Color has been updated'); + + $this->drupalPost('examples/configuration-complex/big_white_kitten/delete', array(), t('Confirm')); + $this->assertNoText('Big white kitten', 'Big white kitten has been deleted, is no longer present'); + + $this->drupalGet('examples/configuration-complex/machine_name_does_not_exist/delete'); + $this->assertText(t('The kitten @kid does not exist and cannot be deleted.', array('@kid' => 'machine_name_does_not_exist')), 'trying to delete a non-existant machien name fails gracefully'); + $this->drupalGet('examples/configuration-complex/machine_name_does_not_exist/update'); + $this->assertText(t('The kitten @kid does not exist and cannot be updated.', array('@kid' => 'machine_name_does_not_exist')), 'trying to update a non-existant machien name fails gracefully'); + } +}