Showing posts with label drupal. Show all posts
Showing posts with label drupal. Show all posts

Drupal: Remove $_GET args from pager

theme_pager() is quite handy, but it will also append alot of form information that you may not need into the page links.

To remove the fields from the URL, prior to your theme_pager() call, unset the variables from $_REQUEST.

// Remove search-button from the pager args
if (isset($_REQUEST['search-button'])) {
unset($_REQUEST['search-button']);
}

After that, they should no longer appear in your pager.

Drupal 6: Change theme for maintenance page

To display your maintenance page in your chosen theme and not the Drupal minelli/garland style, you'll need to create a copy of your page.tpl.php file.

Save the copy as "maintenance-page.tpl.php" in your theme folder and rip out all the bits you don't need.

Now set the 'maintenance_theme' variable using variable_set() to whatever theme you wish. It should apply immediately.

variable_set('maintenance_theme', 'your_theme_name');

Tested on Drupal 6.

Drupal: Paging made easy

This example shows all the information it takes to implement a simple pager.

$sql = "SELECT nid FROM node n WHERE type = '%s' AND status = %d ORDER BY created DESC";
$res = pager_query($sql, 5, 0, NULL, array('node_type_name', TRUE));

while ($res && ($row = db_fetch_array($res))) {
$node = node_load($row['nid']);
// Display node information here
}

echo theme('pager');

The query can be changed easily, as long as you update pager_query() with the right arguments.

theme('pager') displays the pager widget.

Drupal: Redirect create node form to custom page after save

Normally, we'd set $form['#redirect'] within the hook_form_alter() step if we want to edit the redirect.

But for node forms, the 'Save' and 'Preview' buttons change the behaviour, so we'll need to add our own submit handler and do it within the callback.

/*
* Implements hook_form_alter().
*/
function mod_form_alter(&$form, $form_state) {
$form['buttons']['submit']['#submit'][] = 'mod_form_finish_redirect';
unset($form['buttons']['preview']);
}

/**
* Custom submit handler. Overwrites the form redirection variable.
*/
function mod_form_finish_redirect($form, &$form_state) {
$form_state['redirect'] = '/some/other/path';
}

Drupal 6: Remove unnecessary CSS files

Creating a new Drupal theme is fairly simple, but its really annoying when the overly ambitious CSS selectors from "system.css", "node.css", "defaults.css" and "user.css" start messing up your layout.

To get rid of them, override phptemplate_preprocess_page() in your theme "template.php" file and append:

function phptemplate_preprocess_page(&$vars) {
// Remove the stupid Drupal css stuff from being added
unset($vars['css']['all']['module']['modules/node/node.css']);

unset($vars['css']['all']['module']['modules/system/defaults.css']);
unset($vars['css']['all']['module']['modules/system/system.css']);
unset($vars['css']['all']['module']['modules/system/system-menus.css']);
unset($vars['css']['all']['module']['modules/user/user.css']);

$vars['styles'] = drupal_get_css($vars['css']);
}

This code was ported from John Forsythe's Drupal 5 post.

[ Source ]

Drupal: Simple way to bootstrap scripts into Drupal system

Previously I wrote about a method of hooking up an existing site with the Drupal backend. That will work fine if the request comes from the browser, but for a script you will need an extra little bit of configuration.

In conjuction with initialise_drupal_bootstrap() (a function provided in the previous post), you will need to call the following.

function initialise_for_script($server_address = 'yoursite.com') {
global $_SERVER;

// define default settings
$_SERVER['HTTP_HOST'] = $server_address;
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = 'PHP CLI';
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
}

When using the bootstrappers, your script would look something like this:

/*
* Run this from your Drupal folder.
* Usage: php scripts/mailout.php
*/

require_once('./scripts/drupal_bootstrap.inc');
initialise_for_script('examplesite.com');
initialise_drupal_bootstrap();

if (function_exists(mailout_loop')) {
mailout_loop();
}

Drupal: Reuse the node edit form in a page

If you ever need to duplicate the functionality of the path of "/node/%node/edit", you can simply copy the following to and edit so it matches what you need.

In your menu hook, add this menu item:

$items['something/edit/%node'] = array(
  'page callback' => 'something_edit_page',
  'page arguments' => array(2),
  'type' => MENU_CALLBACK,
  'access callback' => 'node_access',
  'access arguments' => array('update', 1),
);

And then create the corresponding callback function to handle the request:

function something_edit_page($node) {
  require_once(drupal_get_path('module', 'node') . '/node.pages.inc');
  return node_page_edit($node);
}

So when you visit "/something/edit/%node", it will display the same things as it would on the normal edit node page (except the "View" and "Edit" tabs).

Drupal: Load JS file from external source

Sometimes you need to use JavaScript API from another source and drupal_add_js() just doesn't cut it.

Instead, just use drupal_set_html_head().

drupal_set_html_head('<script src="http://another-site.net/blah.js" type="text/javascript"></script>');

Drupal: Highlight errors in fieldset with form_set_error()

Using form_set_error() to display error messages, the first argument is the name of a field.

$form['height'] = array(
  '#type' => 'textfield',
  '#title' => 'Height',
);

Along with:

form_set_error('height', 'Error with height.');

image

Normally this is simple to define, but when using nested form items within fieldsets, this doesn't highlight the right item.

If the form was changed to nest the items:

$form['new'] = array(
  '#type' => 'fieldset',
  '#title' => 'New Image Size',
  '#tree' => true,
);

$form['new']['height'] = array(
  '#type' => 'textfield',
  '#title' => 'Height',
);

Using:

form_set_error('height', 'Error with height.');

image

Notice the height is no longer highlighted?

The reason is that the field argument requires some formatting to make it work with nested forms.

Using a stupid '][' to tokenise the fieldname, we can specify which item to highlight.

form_set_error('new][height', 'Error with nested height.');

image

This gives us the proper feedback we wanted.

[ Source ]

Drupal: Using #ahah to modify forms

Often it is useful to dynamically modify forms by adding or removing form elements with Javascript or Ajax. Drupal 6 makes it easy to do that, using the element attribute #ahah which will react to specified events.

Getting started with AHAH (Asychronous HTML and HTTP, which is really just a fancy name for Ajax) is fairly difficult at first, but once you have a reusable pattern it should be fairly easy.

The following example will add a new option into the select dropdown everytime the user clicks add.

Firstly, you need to define the AHAH attribute on the element upon form declaration.

function example_admin_something_form($form_state) {
  $form = array();
  // The dropdown which gets refreshed when new items are added.
  $form['example_dropdown'] = array(
    '#type' => 'select',
    '#title' => 'Example Dropdown',
    '#options' => array(),
    '#prefix' => "<div id='example-select-wrapper'>",
    '#suffix' => '</div>',

  );
  // The magical AHAH button. This does not cause a page reload.
  $form['add'] = array(
    '#type' => 'button',
    '#value' => 'Add',
    '#ahah' => array(
      'event' => 'click',
      'path' => "example/ahah/add_option",
      'wrapper' => "example-select-wrapper",
      'method' => 'replace',
    ),

  );

  // A textfield which allows the user to enter in new select options.
  $form['new_option'] = array(
    '#type' => 'textfield',
    '#title' => t('New Option Name'),
  );

  // Submit button to actually save the form data.
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Save',
  );

  return $form;
}

Create a menu path which links to your menu handler.

function hook_menu() {
  $path['example/ahah/add_option'] = array(
    'page callback' => 'example_ahah_add_option_js',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $path;
}

And now for the handler.

function example_ahah_add_option_js() {
  // Retrieve the cached form
  $form_state = array('submitted' => FALSE);
  $form_build_id = $_POST['form_build_id'];
  $form = form_get_cache($form_build_id, $form_state);
  // -----
  // You can modify the form here to append new options
  $new_option = $_POST['new_option'];
  $options = $form['example_dropdown']['#options'];
  if (!empty($new_option)) {
    // If the key isnt important to you, just use this line.
    // $form['example_dropdown']['#options'][] = $new_option;
    // I use "Array + Array" here as it maintains the keys if necessary.
    // array_merge() does not maintain the keys.
    $form['example_dropdown']['#options'] = $options + array($new_option);
  }
  // End editable code
  // -----
  // Reset the cached form. Without doing this, Drupal will think this
  // is an externally modified form attack and ignore the data.
  form_set_cache($form_build_id, $form, $form_state);
  $form += array('#post' => $_POST, '#programmed' => FALSE);
  $form = form_builder($_POST['form_id'], $form, $form_state);
  // -----
  // Now we should render only the part of the form we wish to update.
  // This will provide the output to place into the wrapper.
  drupal_json(array(
    'status'   => TRUE,
    'data'     => theme('status_messages') . drupal_render($form['example_dropdown']),
  ));
}

Now the form should append a new option, only if text is entered into the textfield. For more options for the #ahah attribute (such as events, effects or progress bars), see the documentation.

[ Sources ]

  • Drupal Documentation for #ahah
    You've probably seen this already, but a good place to start when getting an brief overview.
  • Getting going with AHAH and Drupal 6
    This has nice flowcharts and describes (with images) what the #ahah options are.
  • Drupal 6 AHAH forms: Making New Fields Work
    A bare bones code snippet which works and offers some light explanation. It is a good idea to wrap the reusable code fragments into a new function as he did. Would probably better to rename $form in easy_ahah_form_field() to $field.

    My tutorial is based upon this code fragment, which ignores step #5 (the submit handler step) in the link below.
  • Doing AHAH Correctly in Drupal 6
    Detailed description of how the process works. This is not noob friendly and will hurt your brain.

Drupal: Using #autocomplete_path on textfield elements

Sometimes it is useful to have some sort of auto-complete features when typing. Drupal provides some API for you to hook into in case you want to add it.

Firstly, when declaring your textfield in the form, add the #autocomplete_path attribute in your form declaration.

$form['example'] = array(
  '#type' => 'textfield',
  '#title' => t('Example Textfield'),
  '#autocomplete_path' => 'path/to/callback',
);

Now you need to create a menu callback which hooks the path to the callback in your hook_menu() definition.

$path['path/to/callback'] = array(
  'page callback' => 'module_auto_complete_does_something',
  'type' => MENU_CALLBACK,
);

Finally, you need to create the callback. It will need to return some JSON output, but all we need to worry about is creating an array which with meaningful keys. The values in the array are used for labels. drupal_json() will handle the rest.

function module_auto_complete_does_something($string) {
  $matches = array();
  $res = db_query("SELECT * FROM term_data WHERE name ~* '%s' ORDER BY LOWER(name)", $string);

  while ($row = db_fetch_array($res)) {
    $matches[$row['tid']] = $row['name'];
  }

  // Optional: Sort the results
  // asort($matches);

  drupal_json($matches);
}

And there you have it, a fully working auto complete for a textfield.

Additional arguments:

If you need to pass additional arguments to the callback, you can do so by modifying the menu hook.

$path['path/to/callback/%/%'] = array(
  'page callback' => 'module_auto_complete_does_something',
  'page arguments' => array(3, 4),
  'type' => MENU_CALLBACK,
);

The $string argument is automatically passed to the function by the API. If you wish to add arguments to the callback, place the $string argument as the last parameter.

function module_auto_complete_does_something($arg1, $arg2, $string) {
  ...

Drupal: Caching frequently used data

Often you will need to cache data in your projects to save valuable processing power and reduce hits on the database server.

Drupal provides caching functionality to you via the use of cache_get() and cache_set() functions.

/**
* Retrieves some sort of data.
*/
function module_get_rows($catid) {
  $key = "module_get_rows_$catid";
  $cache = cache_get($key);

  if ($cache) {
    return $cache->data;
  }

  $res = db_query("SELECT * FROM table_data WHERE catid = %d", $catid);
  $rows = array();

  while ($res && ($row = db_fetch_array($res))) {
    $rows[$row['catid'] = $row;
  }

  cache_set($key, $rows, 'cache', time() + 900);
  return $rows;
}

The example does the following:

  1. Create a cache key which is dependant on the $catid argument. This ensures that all the values are cached are depending on the category requested.
  2. Try to get an existing cache value using cache_get().
  3. If the cache is valid, return $cache->data.
  4. Otherwise, generate the cache data.
  5. Store the data using cache_set(). The cache will expire in the current time + 15 minutes, which is written as time() + 900 seconds.
  6. Return the data so the calling function can continue processing.

Drupal 6: Define a numeric field type in the database schema

Often it is useful to define numeric fields in the database for information such as pricing or measurement.

In Drupal, the way to define a numeric type is to also declare the "precision" option.

$schema['products'] = array(
  'description' => t('Stores product data.'),
  'fields' => array(
    'prodid' => array('type' => 'int', 'not null' => TRUE),
    'manufacturer' => array('type' => 'text'),
    'product_name' => array('type' => 'text'),
    'description' => array('type' => 'text'),
    'price' => array('type' => 'numeric', 'precision' => 2),
  ),
  'primary key' => array(prodid'),
);

Using Drupal API in your website

(If you're looking for a way to bootstrap your automated/scheduled scripts, then look here. This guide is for bootstrapping Drupal to pages loaded by your browser.)

As this is written for Drupal 6.8, instructions may vary for other versions (5.x, 7.x, etc).

Download Drupal and extract files into /path/to/website/drupal

1. Installation
Install Drupal by visiting http://domain/drupal/install.php in your browser.
Ensure no table name conflicts with existing site (ie. any tables named "users", "files" or "access")

2. Bootstrapping the Drupal API
[ source ]
This allows full access to Drupal API from without your website.

The following code is a modified version of ceejayoz's script bootstrap code.

/**
* Allows us access into Drupal's API.
*/
function initialise_drupal_bootstrap() {
// Determine Drupal's directory
$DRUPALINC = realpath('./drupal');

// Adjust PHP's include path so Drupal properly includes it's dependencies
$paths = explode(';', get_include_path());
$paths[] = realpath('./');
$paths[] = $DRUPALINC;
set_include_path(implode(';', $paths));

// Save current working directory
$cwd = getcwd();
chdir($DRUPALINC);

// Call Drupal's bootstrapping code
require_once('includes/bootstrap.inc');
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

// restore error reporting to its normal setting
error_reporting(E_ALL);
}

Whenever you need to enable the Drupal API on a page, simply call initialise_drupal_bootstrap().

3. Edit .htaccess
This step is optional for Linux users, as you can simply create a symlink to the paths required.

Copy "/path/to/website/drupal/.htaccess" to "/path/to/website/" and open it up in a text editor.
Uncomment the line with "RewriteBase" and set it to "RewriteBase /drupal"

4. Test
  • Open your main page, it should show your original site. (ie. http://www.domain.com)
  • Attempt to access a Drupal rendered page, such as your user profile page at "http://www.domain.com/user"
  • View the Drupal main page, at "http://www.domain.com/drupal"

5. Configure
Log in as the administrator and view the page "http://www.domain.com/admin/settings/site-information" to set up your default front page.

Can't edit or delete Drupal settings.php

When installing Drupal onto a webhost, I noticed that I couldnt modify the settings.php file once it was configured by the Drupal installer.

For security reasons, the folks at Drupal think its a bad idea to let us edit our own file.

How am I gonna modify the $cookie_domain variable in that file?
Hmm...

I tried to
- gave it permissions using FTP and cPanel (but it didnt work)
- use FTP and delete it (access denied)
- used cPanel file manager to delete it (and it came back?)

While looking through the directory structure, I noticed that the "/sites/all/default" folder also did not have write access.

If you're lucky enough to have the files created in your own username, then using an ftp client or chmod, allow write permissions on both the /sites/all/default folder and /sites/all/default/settings.php file.

Otherwise, you're gonna have to email someone at the hosting facility about it.

Batch migration of users into Drupal

The $new_user array can contain any field with the name matching a field in the "users" database table.
$new_user = array(
'name' => $username,
'pass' => $password,
'mail' => $email,
'status' => true,
);
"mail" can be omitted, but the important one is the username.
Once the data is filled, call user_save() to create the user.
$new_user_object = user_save(NULL, $new_user);
The important part is to give the first parameter NULL, as we want to be creating new users.

Be sure to check "$new_user_object afterwards != FALSE" in case the registration process failed.

This code was checked and working with Drupal 6.x.

Getting Drupal 6 Clean URLs to work with Apache 2

1. Global configuration (httpd.conf)
Search for
#
# This should be changed to whatever you set DocumentRoot to.
#

and change "AllowOverride None" to "AllowOverride All".

You now have the option to load the "rewrite_module" globally (for all sites) or just a specific vhost.

2. a) Load module globally (httpd.conf)
Search for "#LoadModule rewrite_module modules/mod_rewrite.so" and uncomment it.

2. b) Per virtual host (conf/extra/httpd-vhosts.conf)
Search for your vhost and add "LoadModule rewrite_module modules/mod_rewrite.so".

3. Test settings (
http://server.url/admin/settings/clean-urls)
Restart your Apache server and test the settings.
 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog