Archive for the ‘Drupal’ Category

Drupal Notes 7: module custom content type, hook_filter(), custom hook, mail API, sends themed email to all members, creates an action and a trigger

Monday, September 6th, 2010

a few of the hooks utilized in this module:

hook_help(), hook_node_info(), hook_form(), hook_filter(), hook_filter_tips(),
hook_action_info(), hook_theme(), hook_mail(), hook_install()

Biggest concept herein: module_invoke_all()

Step 1: sitenews.info

; $Id$
name = “sitenews”
description = “Sends an email news message to all members”
core = 6.x
php = 5.1
; dependencies = “trigger”

Step 2: sitenews.module

// $Id$
/**
* sitenews Module
* Adds a content type
* Provides an action for admininstrators
* Sends emails to all members
* Defines a new hook and a new filter as well
* @file
*/
/**
* Implementation of hook_help()
*/

function sitenews_help($path, $arg){
if($path == ‘admin/help#sitenews’){
$txt = ‘Keep members current via email.’
. ‘You will need a trigger to the corresponding action.’
. ‘Recommendation: the node publish event should be tied to the action.’;
$replace = array();
return ‘<p>’ . t($txt, $replace) . ‘</p>’;
}
}
/**
* Create the sitenewsbrief’ content type
* Implements hook_node_info()
*/

function sitenews_node_info(){
return array(
‘newsbrief’ => array(
‘module’ => ‘sitenews’,
‘name’ => t(‘News Brief’),
‘description’ => t(“A newsletter to all members”),
‘has_title’ => TRUE,
‘title_label’ => t(‘Title’),
‘has_body’ => TRUE,
‘body_label’ => t(‘News Brief’),
)
);
}
/**
* Create the form for editing sitenewsbrief nodes
* Implements hook_form()
*/

function sitenews_form(&$node){
$type = node_get_types(‘type’, $node);
if($type->has_title){
$form[‘title’] = array(
‘#type’ => ‘textfield’,
‘#title’ => check_plain($type->title_label),
‘#required’ => true,
‘#default_value’ => $node->title,
‘#weight’ => -5,
);
}
if($type->has_body){
$form[‘body_field’] = node_body_field(
$node,
$type->body_label,
$type->min_word_count
);
}
return $form;
}
/**
* Implements hook_filter()
*/

function sitenews_filter($op, $delta = 0, $format = -1, $text = ”){
global $user;

if($op == ‘list’){
$list = array(
0 => t(‘Newsletter Brief Placeholders’),
1 => t(‘Remove ALL HTML/XML Tags’),
);
return $list;
}

switch($delta){
// Delta 0 is for replacing placeholders
case 0:
switch ($op)
{
case ‘description’:
return t(‘Replaces {{salutation}} with newsletter brief ‘
. ‘salutation, and {{brief name}} with the newsletter brief ‘
. ‘name.’);
case ‘prepare’:
return $text;
case ‘process’:
$text = str_replace(
{{salutation}}‘,
variable_get(‘sitenews_salutation’, ‘Dear Community Member’),
$text
);
$text = str_replace(
{{brief name}}‘,
variable_get(‘sitenews_name’, ‘Site News’),
$text
);
return $text;
case ‘settings’:
$form[‘sitenews_filter’] = array(
‘#type’ => ‘fieldset’,
‘#title’ => t(‘Site Newsletter Filters’),
‘#collapsible’ => true,
‘#collapsed’ => false,
);
$form[‘sitenews_filter’][‘sitenews_salutation’] = array(
‘#type’ => ‘textfield’,
‘#description’ => t(‘The greeting’),
‘#title’ => t(‘Salutation’),
‘#default_value’ => variable_get(‘sitenews_salutation’, ‘Dear Community Member,’),
);
$form[‘sitenews_filter’][‘sitenews_name’] = array(
‘#type’ => ‘textfield’,
‘#description’ => t(‘Title of the site newsletter’),
‘#title’ => t(‘Site Newsletter Name’),
‘#default_value’ => variable_get(‘sitenews_name’, ‘Site Newsletter’),
);
return $form;
}
case 1:
switch($op)
{
case ‘description’:
return t(‘Removes all tags (HTML or XML elements).’);
case ‘prepare’:
return $text;
case ‘process’:
return strip_tags($text);
} // end switch for $op
} // end outer switch
}
/**
* Formatting instruction tips for content creators
* Implements hook_format_info()
*/

function sitenews_filter_tips($delta, $format, $long){
switch($delta){
case 0:
$text = “Instances of {{brief name}} will be ”
. “replaced by the global name for site newsletter.”
. ” {{salutation}} will be replaced with the global ”
. “greeting message.”;
if($long){
$text .= “Site Newsletter name and salutation text ”
. “can be configured from the administration interface.”;
}
return $text;
case 1:
$text = “HTML and XML tags will be removed from the final output”;
return $text;
}
}
/**
* Get a format ID by name
* This returns an Input Format ID that can be passed to check_marckup() to filter content
* If a matching format isn’t found, the default format is returned
* @param $name
* String name of the format
* @return
* The ID (an integer) of the format
*/

function sitenews_get_format($name){
$res = db_query(
SELECT format FROM {filter_formats} WHERE name = ‘%s’“,
$name
);
$format = db_fetch_object($res);
if($format){
return $format->format;
} else {
return FILTER_FORMAT_DEFAULT;
}
}
/**
* Implements hook_action_info()
*/

function sitenews_action_info(){
watchdog(‘action‘,”Called sitenews_action_info”);
$actions[‘sitenews_send_action’] = array(
‘type’ => ‘node’,
‘description’ => t(‘Send site news as email to all users.’),
‘configurable’ => FALSE,
‘hooks’ => array(
‘nodeapi’ => array(‘insert’,’update’,’presave’),
)
);
return $actions;
}
/**
* Action: Send an email message to all users
*/

function sitenews_send_action(&$object, $context){
// If not a published sitenews, skip
if(!$object->status || $object->type != ‘newsbrief’){
return;
}
// Get addresses
$q = “SELECT mail, status FROM {users} ”
. “WHERE status != 0 AND uid > 0
“;
$results = db_query($q);
$addresses = array();
while($obj = db_fetch_object($results)){
$addresses[] = $obj->mail;
}
if(count($addresses) == 0){
watchdog(
‘sitenews’,
‘No user email addresses were found’,
array(),
WATCHDOG_ERROR
);
return;
}
// Execute hook_sitenews()
$content = module_invoke_all(‘sitenews’);
// Build params
$params = array(
‘node’ => $object,
‘to’ => implode(‘, ‘, $addresses),
‘subject’ => $object->title,
‘context’ => $context,
‘additional content’ => $content,
);
$message = _sitenews_do_message($object, $params);
watchdog(
actions‘,
‘Site News “Send action fired. Sending to !mail’,
array(‘!mail’ => $params[‘to’])
);
}
/**
* Internal function to prepare a message and pass it on to the mailer
* @param $node
* The news brief node
* @param $params
* An array of params
*/

function _sitenews_do_message(&$node, $params){
$node = $params[‘node’];
$content = $params[‘additional content’];
// Theme the main node:
$params[‘body’] = theme(‘sitenews_newsbrief’, $node);
// See common.inc (element_sort() and drupal_render()
uasort($content, ‘element_sort’);
// Render each block of content;
foreach($content as $item){
$params[‘body’] .= theme(‘sitenews_msgblock’, $item);
}
// Send the mail:
drupal_mail(
‘sitenews’,
‘sendsitenews’,
$params[‘to’],
language_default(),
$params,
variable_get(‘site_mail’,NULL),
TRUE
);
}
/**
* Implements hook_theme()
*/

function sitenews_theme(){
return array(
‘sitenews_msgblock’ => array(
‘arguments’ => array(‘block’ => NULL),
),
‘sitenews_newsbrief’ => array(
‘arguments’ => array(‘node’ => NULL),
),
);
}
/**
* Theme to display a news brief in a sitenews message block
* @param $node
* The news brief node object
*/

function theme_sitenews_newsbrief($node){
$format = sitenews_get_format(‘Tagless text’);
$text = strtoupper(check_markup($node->title, $format)) . “\n\n”;
$text .= check_markup($node->body, $format) . “\n\n”;
return $text;
}
/**
* Theme for email messages
* @param $block
* A block with #title and #body set
*/

function theme_sitenews_msgblock($block){
$msg = array();
if(!empty($block[‘#title’])){
$title = strtoupper($block[‘#title’]);
for ($i = 0; $i < strlen($title); ++$i){
$underline .= ‘=’;
}
$msg[] = $title;
$msg[] = $underline;
}
$msg[] = $block[‘#body’] .”\n”; // <– extra newline
return implode(“\n”, $msg);
}
/**
* Implementation of hook_mail()
*/

function sitenews_mail($key, &$message, $params){
switch($key){
case ‘sendsitenews’:
$message[‘to’] = $params[‘to’];
$message[‘subject’] = $params[‘subject’];
$message[‘body’] = $params[‘body’];
}
}

Step 3: sitenews.install

// $Id$
/**
* Install the newsletter module
* @file
*/
/**
* Implements hook_intall()
*/
function sitenews_install(){
$name = ‘Tagless text’;
// Check to see if the format already exists
$res = db_query(
SELECT name FROM {filter_formats} WHERE name = ‘%s’“,
$name
);
$has_format = db_result($res);
// Create format
if (!$has_format){
db_query(
INSERT INTO {filter_formats} (name) VALUES (‘%s’)“,
$name
);
}
$res = db_query(
SELECT format FROM {filter_formats} WHERE name = ‘%s’“,
$name
);
$format = db_fetch_object($res);
$q = “INSERT INTO {filters} (format, module, delta, weight) ”
. “VALUES(%d, ‘newsletter’, %d, %d)
“;
// First, insert the “Newsletter Brief Placeholders” filter:
db_query($q, $format->format, 0, 10);
// Second, insert the “Remove ALL HTML/XML Tags” filter:
db_query($q, $format->format, 1, 0);
}

Step 4: add to module_sitenews() to any modules that will be contributing content to these automatically generated email newsletters, example from personality module (Drupal Notes 6) follows:

function personality_theme(){
return array(
‘personality_info’ => array(
‘template’ => ‘personality_info’,
‘arguments’ => array(
‘dates’ => NULL,
‘life’ => NULL,
‘works’ => NULL,
),
),
// NEW
‘personality_sitenews’ => array(
‘arguments’ => array(‘node’ => NULL),
),
);
}
/**
* Theme function for sitenews
*/

function theme_personality_sitenews($node){
$options = array(‘absolute’ => TRUE);
$url = url(‘node/’. $node->nid, $options);
$title = strip_tags($node->title);
$body = strip_tags($node->teaser); // <– Summary
$text = implode(“\n”, array($title, $body, $url));
$text .= “\n”;
return $text;
}

/*
* Implements hook_sitenews() in sitenews module
*/

function personality_sitenews(){
$q = “SELECT nid, created FROM {node} ”
. “WHERE status = 1 AND type = ‘personality’ ”
. “ORDER BY created DESC
“;
$results = db_query_range($q, 0, 3);
$new_bios = array();
while ($row = db_fetch_object($results)){
$node = node_load($row->nid);
$new_bios[] = theme(‘personality_sitenews’, $node);
}
$content[‘personality’] = array(
‘#weight’ => 0,
‘#title’ => t(‘Recent Biographies’),
‘#body’ => implode(“\n”, $new_bios),
);
return $content;
}

Drupal Notes 6: module + new content type (non CCK)

Sunday, September 5th, 2010

Description: Module that uses a new content type (non CCK), that uses the Schema API, Node API, Database API, Forms API and the following hooks:

hook_form(), hook_install(), hook_uninstall(), hook_schema(), hook_node_info(), hook_perm(), hook_access(), hook_insert(), hook_update(), hook_delete(), hook_nodeapi(), hook_load(), hook_view(), hook_theme()

Step 1: sites/all/modules/personality folder

Step 2: personality.info

; $Id$
name = “Personality Content (Node) Type”
description = “This provides a custom content type to store simple online personalities”
core = 6.x
php = 5.1

Step 3: personality.install

// $Id$
/**
* Install the personality module, including its content (node) type.
* @file
*/

/**
* Implementation of hook_install()
*/

function personality_install(){
drupal_install_schema(‘personality’);
}
/**
* Implementation of hook_uninstall()
*/

function personality_uninstall(){
drupal_uninstall_schema(‘personality’);
}
/**
* Implementation of hook_schema()
*/

function personality_schema(){
$schema[‘personality’] = array(
‘fields’ => array(
‘vid’ => array(
‘type’ => ‘int’,
‘unsigned’ => TRUE,
‘not null’ => TRUE,
‘default’ => 0,
),
‘nid’ => array(
‘type’ => ‘int’,
‘unsigned’ => TRUE,
‘not null’ => TRUE,
‘default’ =>0,
),
‘dates’ => array(
‘type’ => ‘varchar’,
‘length’ => 127,
‘not null’ => TRUE,
‘default’ => ”,
),
// Note: On MySQL, text fields cannot have default values
‘life’ => array(
‘type’ => ‘text’,
‘not null’ => FALSE
),
‘works’ => array(
‘type’ => ‘text’,
‘not null’ => FALSE
),
),
‘indexes’ => array(
‘nid’ => array(‘nid’),
),
// Version is primary key.  Could do nid, vid.
‘primary key’ => array(‘vid’),
);
return $schema;
}

Step 4: personality.module

// $Id$
/**
* Provides the Personality content type
* @file
*/
/**
* Implementation of hook_help()
*/
function personality_help($path, $arg){
if($path == ‘admin/help#personality’){
$txt = ‘A personality is a description of a person\’s person.  Should include a brief summary, biography, date of birth, optionally date of death, and accomplishments.’;
$replace = array();
return ‘<p>’ . t($text, $replace) . ‘</p>’;
}
}
/**
* Imnplementation of hook_node_info()
*/

function personality_node_info(){
return array(
‘personality’ => array(
‘name’ => t(‘Personality’),
‘module’ => ‘personality’,
‘description’ => t(‘A personality of a person.’),
‘has_title’ => TRUE,
‘title_label’ => t(‘Personality of’),
‘has_body’ => TRUE,
‘body_label’ => t(‘Overview’),
)
);
}
/**
* Implementation of hook_form()
*/

function personality_form(&$node){
$type = node_get_types(‘type’, $node);
//Existing files: title (Personality of) and body (Overview)
if($type->has_title){
$form[‘title’] = array(
‘#type’ => ‘textfield’,
‘#title’ => check_plain($type->title_label),
‘#required’ => TRUE,
‘#default_value’ => $node->title,
‘#weight’ => -5,
);
}
if($type->has_body){
$form[‘body_field’] = node_body_field(
$node,
$type->body_label,
$type->min_word_count
);
}
// Our custom fields: Dates, Life, Works
$form[‘dates’] = array(
‘#type’ => ‘textfield’,
‘#size’ => 50,
‘#maxlength’ => 127,
‘#title’ => t(‘Dates’),
‘#description’ => t(‘Birth and death dates.’),
‘#default_value’ => isset($node->dates) ? $node->dates : ”,
);
$form[‘life’] = array(
‘#type’ => ‘textarea’,
‘#title’ => t(‘Life’),
‘#cols’ => 50,
‘#rows’ => 15,
‘#description’ => t(‘A description of this perons\’s life.’),
‘#default_value’ => isset($node->life) ? $node-life : ”,
);
$form[‘works’] = array(
‘#type’ => ‘textarea’,
‘#title’ => t(‘Works’),
‘#cols’ => 50,
‘#rows’ => 5,
‘#description’ => t(‘This person\’s Personality’),
‘#default_value’ => isset($node->works) ? $node->works : ”,
);
return $form;
}
/**
* Implements hook_perm()
* Responsible for registering new permissions
*/

function personality_perm(){
return array(
‘create personality node’,
‘edit personality nodes’,
‘delete personality nodes’,
);
}
/**
* Implementation of hook_access()
* which implements our new permissions
*/

function personality_access($op, $node, $account){
switch($op){
case ‘create’:
return user_access(‘create personality node’, $account);
case ‘update’:
return user_access(‘edit personality nodes’, $account);
case ‘delete’:
return user_access(‘delete personality nodes’, $account);
}
}
/**
* Implements hook_insert()
*/

function personality_insert($node){
if(!isset($node->life)){
$node->life = ”;
}
if(!isset($node->works)){
$node->works = ”;
}
db_query(
INSERT INTO {personality} (vid, nid, dates, life, works) VALUES (%d, %d, ‘%s’, ‘%s’, ‘%s’)“,
$node->vid,
$node->nid,
$node->dates,
$node->life,
$node->works
);
}
/**
* Implements hook_update()
*/

function personality_update($node){
if($node->revision){
personality_insert($node);
}
else{
db_query(
UPDATE {personality} SET dates = ‘&s’, life = ‘%s’, works = ‘%s’ WHERE vid = %d“,
$node->dates,
$node->life,
$node->works,
$node->vid
);
}
}
/**
* Implements hook_delete()
*/

function personality_delete($node){
db_query(
DELETE FROM {personality} WHERE nid = %d“,
$node->nid
);
}
/**
* This implementation deletes node revisions
* Implements hook_nodeapi()
*/

function personality_nodeapi(&$node, $op, $teaser, $page){
if($op == ‘delete revision’){
db_query(
DELETE FROM {personality} WHERE vid = %d‘,
$node->vid
);
}
}
/**
* Implementation of hook_load()
*/

function personality_load($node){
$result = db_query(
SELECT dates, life, works FROM {personality} WHERE vid = %d‘,
$node->vid
);
return db_fetch_object($result);
}
/**
* Implementation of hook_view()
*/

function personality_view($node, $teaser = FALSE, $page = FALSE){
$node = node_prepare($node, $teaser);
$dates = check_plain($node->dates);
$life = check_markup($node->life);
$works = check_markup($node->works);
// Add theme
$node->content[‘personality_info’] = array(
‘#value’ => theme(‘personality_info’, $dates, $life, $works),
‘#weight’ => 1,
);
return $node;
}
/**
* Implements hook_theme()
*/

function personality_theme(){
return array(
‘personality_info’ => array(
‘template’ => ‘personality_info’,
‘arguments’ => array(
‘dates’ => NULL,
‘life’ => NULL,
‘works’ => NULL,
),
),
);
}

Step 5: personality_info.tpl.php template theme file

// $Id$
/**
* Template to display personality nodes.
*
* Fields available:
* $dates: cleaned plain text string
* $life: cleaned HTML string
* $works: cleaned HTML string
*/

<div>
<h2>< ? php print t(‘Dates’); ? >:</h2>
< ? php print $dates; ? >
<h2>< ? php print t(‘Life’); ? >:</h2>
< ? php print $life; ? >
<h2>< ? php print t(‘Works’); ? >:</h2>
< ? php print $works; ? >
</div>

Drupal Notes 5: admin module emails users from profile: hook_mail(), hook_mail_alter(), hook_help(), hook_menu(), Forms API, hook_user()

Saturday, September 4th, 2010

For this module it’s best that your development environment is configured to send live emails.
If it’s local, xampp perhaps, and not currently configured as such,
you may want to refer to this post first:
http://alexyz.com/sending-mail-xampp-gmail/

Step 1, of course, create a new folder entitled emailusers in sites/all/modules

Step 2, in that folder, create emailusers.info, containing the following
(if you’ve read Notes 1-4 this should all be very familiar already):

;$Id$
name = “emailusers”
description = “admin module for emailing users from within their profile”
core = 6.x
php = 5.1

Step 3, create emailusers.module in same folder, contents thereof follow:

// $Id$
/**
* This module provides an email interface for administrators.
* Using this module, administrators can send email to a user from the
* user’s “view” page.
* @file
*/
/**

* Implementation hook_help()
*/

function emailusers_help($path, $arg){
if($path == ‘admin/help#emailusers’){
$txt = ‘This module provides a way for an administrator to send’
. ’email to a user. ‘
. ‘It assumes that the Drupal mailer is configured.’;
return ‘<p>’ . t($txt) . ‘</p>’;
}
}
/**
* Implementation of hook_munu()
*/

function emailusers_menu(){
// Need to pass User ID via placeholder %,
// thus URL will be available here: ?q=admin/emailusers/compose/userIdHere

$items[‘admin/emailusers/compose/%‘] = array(
‘title’ => ‘Compose a Message’,
‘page callback’ => ’emailusers_compose’,
‘page arguments’ => array(3), // <- userId (from % in node path)
‘access arguments’ => array(‘administer users’),
‘type’ => MENU_CALLBACK,
);
return $items;
}
/**
* Compose a message.
* This creates the form necessary to compose an email message.
*
* @param $to
* The address to send to.
* @return
* HTML.
*/

function emailusers_compose($userid){
$userid = intval($userid);
if($userid == 0){
return t(‘User ID must be an integer.’);
}
$account = user_load($userid);
if(empty($account)){
return t(‘No such user found.’);
}
$to = $account->mail;
$sb = ‘<p>’
. t(‘Send a message to @email.’, array(‘@email’ => $to))
. ‘</p>’;
$sb .= drupal_get_form(’emailusers_compose_form’, $account);
// Forms API / FAPI
// http://drupal.org/node/751826
// http://api.drupal.org/api/drupal/developer–topics–forms_api_reference.html/6

return $sb;
}
/**
* Form constructor
*/

function emailusers_compose_form($context, $account){
// This is a value only — equivalent to a hidden field
// except that it is never renered into the HTML

$form[‘to’] = array(
‘#type’ => ‘value’,
‘#value’ => $account,
);
// Create a fieldset for the body:
$form[‘message’] = array(
‘#type’ => ‘fieldset’,
‘#title’ => t(‘Compose the Message’),
);
// Textfield for subject of the body
$form[‘message’][‘subject’] = array(
‘#type’ => ‘textfield’,
‘#title’ => t(‘Subject’),
‘#size’ => 50,
‘#maxlength’ => 255,
‘#description’ => t(‘The subject of the email message.’),
);
// And a text area for the body.
$form[‘message’][‘body’] = array(
‘#type’ => ‘textarea’,
‘#title’ => t(‘Message’),
‘#cols’ => 50,
‘#rows’ => 5,
‘#description’ => t(‘The body of the email message.’),
);
// Create a fieldset for details
$form[‘details’] = array(
‘#type’ => ‘fieldset’,
‘#title’ => t(“Details”),
);
// Checkbox: if checked, CC the author too
$form[‘details’][‘cc_me’] = array(
‘#type’ => ‘checkbox’,
‘#title’ => t(‘BCC Yourself’),
‘#default_value’ => 1,
‘#description’ => t(‘If this is checked, the message will also be sent to you.’),
);
// Finally, a submit button:
$form[‘submit’] = array(
‘#type’ => ‘submit’,
‘#value’ => t(‘Send Mail’),
);
return $form;
}
/**
* Form submission handler, which functions like a hook.
* Note that the params $form and &$form_state are new in D6.
* They replace $form_id and $form_values.
*/

function emailusers_compose_form_submit($form, &$form_state){
$form_values = $form_state[‘values’];
$account = $form_values[‘to’];
drupal_mail(
’emailusers’,
‘composemessage’,
$account->mail,
user_preferred_language($account),
$form_values,
variable_get(‘site_mail’, null),
true // Automatically send
);
// drupal_mail will invoke hook_mail(), or in this case, emailusers_mail(), below
$form_state[‘redirect’] = sprintf(‘user/%d’, $account->uid);
}
/**
* Implementation of hook_mail()
*/

function emailusers_mail($key, &$message, $params){
// Just catch calls to this hook from compose form.
if ($key == ‘composemessage’){
$language = $params[‘language’];
$account = $params[‘to’];
if($params[‘cc_me’]){
// Look up current user’s email address:
//$my_account = user_load(null);

$message[‘headers’][‘bcc’] = $GLOBALS[‘user’]->mail;
}
$message[‘to’] = $account->mail;
$message[‘subject’] = t(‘Drupal Message: ‘, array(), $language->language);
// If these were automatically-generated messages
// they should be run through t()
// but since text is user-entered
// don’t use t()

$message[‘subject’] .= $params[‘subject’];
//$message[‘body’] = $params[‘body’];
$message[‘body’][] = $params[‘body’];
}
}
/**
* Implements hook_mail_alter()
* !! Note: similar, and also of interest are hook_link_alter() & hook_menu_alter()
* simply put, _alter hooks alter data right after their parent hooks are called, but before output
* also important to note: this _alter() below will augment ALL emails sent to the Mail API
* not just those from this module!
*/

function emailusers_mail_alter(&$message){
$append = “\n==================================\n”
. “This message was sent from !site_name (!website). ”
. “If you believe this message to be a case of abuse, ”
. “please contact !site_email.\n”;
$args = array(
‘!website’ => url(”, array(‘absolute’ => true)),
‘!site_email’ => variable_get(‘site_mail’, null),
‘!site_name’ => variable_get(‘site_name’, ‘Unknown’),
);
$message[‘body’] .= t($append, $args);
//$message[‘body’][] = t($append, $args);
}
/**
* Implementation of hook_user().
*/

function emailusers_user($op, &$edit, &$account, $category){
if($op == ‘view’ && user_access(‘administer users‘)){
// Create the outer “block”
$account->content[‘EmailUsers’] = array(
‘#type’ => ‘user_profile_category‘,
‘#attributes’ => array(‘class’ => ‘user-member’),
‘#weight’ => 0,
‘#title’ => t(‘Contact user’),
);
// Create the content of the block
$account->content[‘EmailUsers’][‘EmailLink’] = array(
‘#type’ => ‘user_profile_item‘,
‘#title’ => t(‘Send a message to this user from the site administrator.’),
‘#value’ => l(
‘Email’,
‘admin/emailusers/compose/’ . $account->uid
),
);
}
}

Drupal Notes 4: enhance our module with jQuery AJAX/JSON

Friday, September 3rd, 2010

create quoteDisplay.js to contain:

var quoteDisplay = {}; // JS Namespace Object Creation
if(Drupal.jsEnabled){
$(document).ready(
function(){
$(“#quoteDisplay-origin”).after(“<a>Next &raquo;</a>”).next().click(quoteDisplay.randQuote);
}
);
/**
* A function to fetch quotes from the server,
* and display in the designated area.
*/

quoteDisplay.randQuote = function(){
$.get(Drupal.settings.quoteDisplay.json_url, function(data){
myQuote = Drupal.parseJson(data);
if(!myQuote.status || myQuotes.status == 0){
$(“#quoteDisplay-origin”).text(myQuote.quote.origin);
$(“#quoteDisplay-text”).text(myQuote.quote.text);
}
}); // End inline function
}
}

add this line to quoteDisplay.module file’s theme_quoteDisplay() function (after “drupal_add_js” line, before “$output = ‘

drupal_add_js($module_path . ‘/quoteDisplay.js’);
$opts = array(‘absolute’ => TRUE);
$json_url = url(‘quoteDisplay.json’, $opts);
drupal_add_js(
array(‘quoteDisplay’ =>
array(“json_url” => $json_url)), ‘setting‘);

also add these functions to our quoteDisplay.module file:

/*
* Callback to handle requests for
quoteDisplay content.
* @return
* JSON data.
*/

function quoteDisplay_item(){
$item = _quoteDisplay_get_quote();
drupal_set_header(‘Content-Type: text/plain; charset: utf-8’);
printf(
{ “quote”: { “origin”: “%s”, “text”: “%s” } }‘,
$item->title,
$item->body
);
}

/**
* Implementation of hook_menu()
* registers quoteDisplay.json
* will call quoteDisplay_item() when called
* anyone that may access content may access this path
* MENU_CALLBACK below is a bitmask,
* that doesn’t authomatically create links to this item in the menu or elsewhere,
* it simnply makes this accessible by URL,
* more than 20 such bitmask types may be used,
* see documentation.
*/

function quoteDisplay_menu(){
$items[‘quoteDisplay.json’] = array(
‘title’ => ‘Quote Display AJAX Gateway’,
‘page callback’ => ‘quoteDisplay_item’,
‘access arguments’ => array(‘access content’),
‘type’ => MENU_CALLBACK,
);
return $items;
}

now hit the url to see your JSON return! (you may need to clear your drupal cache of course)
http://localhost/yourDrupalSiteName/?q=quoteDisplay.json

Drupal Notes 3: a themed module for a new content type

Wednesday, September 1st, 2010

create a new content type, create some entries
create a new theme (see earlier notes for how)

quoteDisplay.info contains the following:

; $Id$
name = “Quote Display”
description = “Dynamic display of  quotes.”
core = 6.x
php = 5.1

quoteDisplay.module contains this:

// $Id$
/**
* @file
* Module for dynamic display of pithy philosophy quotes.
*/

/**
* Implementation of hook_help()
*/

function quoteDisplay_help($path, $arg)
{
if ($path == ‘admin/help#quoteDisplay’)
{
$txt = ‘This module displays philosophical quotes in blocks. ‘ .
‘It assumes the existence of a content type named “quote”.’;
return ‘<p>’ . t($txt) . ‘</p>’;
}
}

/**
* Implementation of hook_block()
*/

function quoteDisplay_block($op = ‘list’, $delta = 0, $edit = array())
{
switch($op)
{
case ‘list’:
$blocks[0][‘info’] = t(‘Philosophical Quotes’);
return $blocks;
case ‘view’:
$item = _quoteDisplay_get_quote();
if(!empty($item))
{
$content = theme(‘quoteDisplay_quote’,
check_plain($item->body),
check_plain($item->title));
$blocks[‘subject’] = t(‘Pithy Quote’);
$blocks[‘content’] = $content;
return $blocks;
}
}
}

/*
* Return a quote from the Drupal database
*/

function _quoteDisplay_get_quote()
{
$sql = “SELECT nid FROM {node} “.
“WHERE status = 1 AND type = ‘quote’ ORDER BY RAND() LIMIT 1”;
$res = db_query($sql);
$item = db_fetch_object($res);
$quote = node_load($item->nid);
return $quote;

}

/*
* Implementation of hook_theme().
*/

function quoteDisplay_theme()
{
return array(
‘quoteDisplay_quote’ => array(
‘arguments’ => array(
‘text’ => NULL,
‘origin’ => NULL
),
),
);
}

/*
* Theme function for theming quotes.
*
* @param $text
* The quote content as a string.
* @param $origin
* The original source of the quote, as a string.
* @return
* An HTML themed string.
*/

function theme_quoteDisplay_quote($text, $origin)
{
$module_path = drupal_get_path(‘module’,’quoteDisplay‘);
$full_path = $module_path . ‘/quoteDisplay.css’;
drupal_add_css($full_path);
$output = ‘<div id=”quoteDisplay-text”>’ . t($text) . ‘</div><div id=”quoteDisplay-origin”>’ . t($origin) . ‘</div>’;
return $output;
}

quoteDisplay.css (also in /sites/all/modules/quoteDisplay) contains:

#quoteDisplay-text:first-letter{
font-size:18pt;
font-weight:bold;
}
#quoteDisplay-origin{
font-style: oblique;
text-align: right;
margin-right: 5px;
}

Note:

This module thus has a default “theme” of its own.  This may be overridden via CSS, or via a quoteDisplay.tpl.php file placed within the themes/module/currentTheme folder

Drupal Notes 2: sub-theme derivative of bluemarine : Theme inheritance, overriding, preprocessing

Sunday, August 29th, 2010

add folder to:
sites/all/themes(createThis)/subThemeName(createThis)/

create subThemeName.info (in newly created folder):
; $Id$
name = subThemeName
description = blah blah blah blah blah
version = 1.0
core = 6.x
base theme = bluemarine
stylesheets[all][] = descartes-style.css // copy images from parent theme into sub-theme.
stylesheets[all][] = new.css

finally, create above mentioned new.css (in newly created folder) and add styles you’d like to modify, example:
/*
** Style to override the styles.css in
** bluemarine.css
*/

/*
* Plain white right nav.
*/

#sidebar-right{
border-left: 1px solid #ea940c;
background-color: white;
}

ready to go further? copy any of the following over from bluemarine into yourNewSubTheme and edit away:
page.tpl.php (hint: start here), block.tpl.php, box.tpl.php, comment.tpl.php

even further, you’ve moved $breadcrumb in page.tpl.php to where you’d like it, but you want to edit INSIDE that…
these PHPTemplate functions that render content can be found in yourSite/includes/theme.inc
find the one that spits out the content you’d like to modify, and then create a template.php in your new theme directory and override that function…
example: theme_breadcrumb() uses “>>” as separators, in
yourSite/sites/all/themes/yourNewTheme/template.php (make it)
overwrite theme_breadcrumb() via something like this:


// $Id$
/**
* @page
* Override theme_() functions for the youNewTheme.
*/

// Overrides theme_breadcrumb()
function phptemplate_breadcrumb($breadcrumb){
if (empty($breadcrumb)) return;
$sep = ‘<div>&nbsp;&nbsp;</div>’;
$breadcrumb_string = ‘<div>’ . implode($sep, $breadcrumb) . ‘</div>’;
return $breadcrumb_string;
} // end of template.php example

other PHPTemplate theme functions from includes/theme.inc to investigate:
theme_image(), theme_links(), theme_progress_bar(), theme_username(), theme_table(), theme_item_list()

called as follows:
$variable = theme(‘functionName’, $arg1, $arg2, …) , which looks like this:
$crumb = theme(‘item_list’, $breadcrumb, null, ‘ul’, array(‘class’=>’breadcrumb-items’));

IMPORTANT NOTES/REMEMBER:
– a phptemplate_block() function redefined in template.php will override a block.tpl.php file in template folder!
– if you want to preprocess CONTENT before it’s passed to template.php, thus, instead use phptemplate_preprocess_functions()!
– if you’re NOT making a sub-theme, add to .info this line: engine=phptemplate instead of using the base theme line
– don’t forget to make a nice screenshot.png (150×90)

Drupal Notes 1: module hook_block(), hook_help(), check_plain(), check_url(), watchdog(), t(), l(), placeholders(!,%,@)

Sunday, August 29th, 2010

Create:
moduleName.info & moduleName.info, save them in sites, all, modules

.info example content:
;$Id$ // This will be substituted by Drupal CVS
name = “ModuleNameHere”
description = “Displays items blah blah blah”
core = 6.x
php = 5.1

.module beginnings…:
// $Id$ // This will be substituted by Drupal CVS
/**
* @file
// This denotes that this comment refers to this whole file
* Description Here Of Module…
* @see http://www.goodreads.com // Drupal doc is generated by doxygen, this links to a ref
*/

/**
* Implementation of hook_block()
*/

function moduleName_block($op=’list’, $delta=0, $edit=array()){
switch($op){
case ‘list’:
$blocks[0][‘info’] = t(‘Module Info Title’);
return $blocks;
case ‘view’:
$url = ‘http://www.moduleXML.com/review/list_rss/’;
$blocks[‘subject’] = t(‘Module Subject’);
$blocks[‘content’] = “Module Content Dynamic or Static…”;
return $blocks;
}
}

/**
* Implementation of hook_help()
*/

function moduleName_help($path, $arg){
if($path == ‘admin/help#moduleName){
$txt = ‘The moduleName module uses the !subThisHolder_url API ‘;
$link = l(‘copyForTheAnchorTag’, ‘http://www.moduleName.com’);
$replace = array(!subThisHolder_url‘ => $link);
return ‘<p>’ . t($txt, $replace) . ‘</p>’;
}
} //end of example .module code…

Other things useful of note:

The watchdog() function: http://api.drupal.org/api/function/watchdog/6
watchdog(‘loggingCategoryNameThisModuleNameMostLikely’, $msg, $varsToSubIntoMsg, WATCHDOG_WARNING):
logging options:
WATCHDOG_EMERG
WATCHDOG_ALERT
WATCHDOG_CRITICAL
WATCHDOG_ERROR
WATCHDOG_WARNING
WATCHDOG_NOTICE
WATCHDOG_INFO
WATCHDOG_DEBUG

check_plain()http://api.drupal.org/api/function/check_plain/6

check_url() http://api.drupal.org/api/function/check_url/6

t() example:

t(‘Replacing %value by !urlHere for @emailPerhaps ‘, array(‘%value’=>’test’, ‘@emailPerhaps’=>$email, ‘!urlHere’=>’http://blah.com’);

!placeholders replaced as is, @placeholders replaced effectively “escaped by” check_plain(), %placeholders are replaced themed as well