Archive for September, 2010

Zend Notes: Grabbing a resource from the registry via a view helper

Wednesday, September 29th, 2010

Helper (application/views/helpers):

class Zend_View_Helper_ImageUrlHelper extends Zend_View_Helper_Abstract {
/**
* Helper to grab the path to the images directory
* @param String $imageName
* @param String $imageSize, var for possibly different size requests
*/

public function ImageUrlHelper($imageName, $imageSize) {
$config = Zend_Registry::get(‘config’);
$path = $config-> urlpaths-> productImages;
return $path . ‘/products_’ . $imageSize . ‘/’ . $imageName;
}
}

application.ini (application/configs):

urlpaths.productImages = “/path/images/product_images”

from a view (application/views/scripts/controllerName/viewName.phtml):

<?php $imgSrc = $this-> ProductImageHelper($this-> products[$i][‘products_image’], ‘100’); ?>

<img border=”0″  src=”<?php echo $imgSrc; ?>” />

framework.zend.com “Create a Model and Database Table” tutorial fixed, full working code, adapted for MySQL

Monday, September 20th, 2010

http://framework.zend.com/manual/1.10/en/learning.quickstart.create-model.html

Well, I agonized over getting this, and if you read the comments on the tutorial, many others have as well, working on my home Snow Leopard as well as on my work XP pc, and I spent countless hours looking for a post just such as this one is now, to no avail, so here it all is, hope it helps somebody:

hosts

In the event that you don’t have this done already, here is what should go in your environment’s hosts file…

Mac: /private/etc/hosts

PC:  C:\WINDOWS\system32\drivers\etc

# Setup “components” Virtual Host
<VirtualHost *:80>
ServerName project
DocumentRoot “C:\xampp\htdocs\project\public// or /Library/WebServer/Documents/project/public

<Directory “C:\xampp\htdocs\project\public” // or /Library/WebServer/Documents/project/public>
Options Indexes FollowSymLinks Includes
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

create the database that this will use

CREATE TABLE IF NOT EXISTS `guestbook` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(32) DEFAULT NULL,
`comment` text,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

add dummy data to the database

INSERT INTO `guestbook` (`id`, `email`, `comment`, `created`) VALUES
(1, ‘ralph@schindler.com’, ‘hello’, ‘2010-09-17 00:00:00’),
(2, ‘foo@bar.com’, ‘goodbye’, ‘2010-09-17 00:00:00’);

if you haven’t done so already, create the user this app will use to connect, and give the user the priviledges they’ll need

CREATE USER ‘guestbook’@’localhost’ IDENTIFIED BY ‘guestbook’;

GRANT ALL ON ‘guestbook’ TO ‘guestbook’@’localhost’;

project/application/Bootstrap.php

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function _initAutoloader()
{
// Create an resource autoloader component
$autoloader = new Zend_Loader_Autoloader_Resource(array(
‘basePath’    => APPLICATION_PATH,
‘namespace’ => ‘Application’
));

// Add some resource types
$autoloader->addResourceTypes(array(
‘forms’ => array(
‘path’ => ‘forms’,
‘namespace’ => ‘Form’
),
‘models’ => array(
‘path’ => ‘models’,
‘namespace’ => ‘Model’
),
));

// Return to bootstrap resource registry
return $autoloader;
}

protected function _initActionHelpers()
{
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH .’/helpers’);
}

protected function _initDoctype()
{
$this->bootstrap(‘view’);
$view = $this->getResource(‘view’);
$view->doctype(‘XHTML1_STRICT’);
}
}

project/public/index.php

<?php
// Define path to application directory
defined(‘APPLICATION_PATH’)
|| define(‘APPLICATION_PATH’, realpath(dirname(__FILE__) . ‘/../application’));

// Define application environment
defined(‘APPLICATION_ENV’)
|| define(‘APPLICATION_ENV’, (getenv(‘APPLICATION_ENV’) ? getenv(‘APPLICATION_ENV’) : ‘development’));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . ‘/../library’),
get_include_path(),
)));

/** Zend_Application */
require_once ‘Zend/Application.php’;

// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . ‘/configs/application.ini’
);
$application->bootstrap()
->run();

project/application/configs/application.ini

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH “/../library”
bootstrap.path = APPLICATION_PATH “/Bootstrap.php”
bootstrap.class = “Bootstrap”
appnamespace = “Application”
resources.frontController.controllerDirectory = APPLICATION_PATH “/controllers”
resources.frontController.params.displayExceptions = 0
resources.view[] =
resources.layout.layoutPath = APPLICATION_PATH “/layouts/scripts/”

resources.db.adapter = “PDO_MYSQL”
resources.db.params.host = “localhost”
resources.db.params.username = “guestbook”
resources.db.params.password = “guestbook”
resources.db.params.dbname = “guestbook”

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.params.displayExceptions = 1

project/application/controllers/GuestbookController.php

<?php
class GuestbookController extends Zend_Controller_Action
{

public function init()
{
/* Initialize action controller here */
}

public function indexAction()
{
$guestbook = new Application_Model_GuestbookMapper();
$guestbook->fetchAll();
//var_dump($guestbook);
$this->view->entries = $guestbook->fetchAll();
}
}

project/application/models/Guestbook.php

<?php
class Application_Model_Guestbook
{
protected $_comment;
protected $_created;
protected $_email;
protected $_id;

public function __construct(array $options = null)
{
if (is_array($options)){
$this->setOptions($options);
}
}

public function __set($name, $value)
{
$method = ‘set’ . $name;
if ((‘mapper’ == $name) || !method_exists($this, $method)){
throw new Exception(‘Invalid guestbook property’);
}
$this->$method($value);
}

public function __get($name)
{
$method = ‘get’ . $name;
if ((‘mapper’ == $name) || !method_exists($this, $method)){
throw new Exception(‘Invalid guestbook property’);
}
return $this->$method();
}

public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach($options as $key => $value){
$method = ‘set’ . ucfirst($key);
if (in_array($method, $methods)){
$this->$method($value);
}
}
}

public function setComment($text)
{
$this->_comment = (string) $text;
return $this;
}

public function getComment()
{
return $this->_comment;
}

public function setEmail($email)
{
$this->_email = (string) $email;
return $this;
}

public function getEmail()
{
return $this->_email;
}

public function setCreated($ts)
{
$this->_created = $ts;
return $this;
}

public function getCreated()
{
return $this->_created;
}

public function setId($id)
{
$this->_id = (int) $id;
return $this;
}

public function getId()
{
return $this->_id;
}
}

project/application/models/GuesbookMapper.php

<?php
class Application_Model_GuestbookMapper
{
protected $_dbTable;

public function setDbTable($dbTable)
{
if (is_string($dbTable)){
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract){
throw new Exception(‘Invalid table data gateway provided’);
}
$this->_dbTable = $dbTable;
return $this;
}

public function getDbTable()
{
if (null === $this->_dbTable){
$this->setDbTable(‘Application_Model_DbTable_Guestbook’);
}
return $this->_dbTable;
}

public function save(Application_Model_Guestbook $guestbook)
{
$data = array(
’email’    =>    $guestbook->getEmail(),
‘comment’    =>    $guestbook->getComment(),
‘created’    =>    date(‘Y-m-d H:i:s’),
);

if (null === ($id = $guestbook->getId())){
unset($data[‘id’]);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data,array(‘id = ?’ => $id));
}
}

public function find($id, Application_Model_Guestbook $guestbook)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)){
return;
}
$row = $result->current();
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
}

public function fetchAll()
{
$resultSet = $this->getDbTable()->fetchAll();
//var_dump($resultSet);
$entries = array();
foreach ($resultSet as $row){
$entry = new Application_Model_Guestbook();
$entry->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
$entries[] = $entry;
}
return $entries;
}
}

project/application/models/DbTable/Guestbook.php

<?php
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
/** Table name */
protected $_name    = ‘guestbook’;
}

project/application/views/scripts/guestbook/index.phtml

<p><a href=”<?php echo $this->url(
array(
‘controller’ => ‘guestbook’,
‘action’ => ‘sign’
),
‘default’,
true) ?>“>Sign Our Guestbook</a></p>

Guestbook Entries: <br />
<dl>
<?php foreach ($this->entries as $entry): ?>
<dt><?php echo $this->escape($entry->email) ?></dt>
<dd><?php echo $this->escape($entry->comment)?></dd>
<?php endforeach ?>
</dl>

final URL to hit: http://project/guestbook

MySQL Mac osx Terminal change root password

Friday, September 17th, 2010

Silly thing to note, but I had to Google for this too long to do it again…

Have to give credit where credit is due, thank you:

http://www.cyberciti.biz/faq/mysql-change-root-password/

conclusion, command:

mysqladmin -u root password NEWPASSWORD

Mac osx Snow Leopard httpd.conf last line error needs removing for virtualhosts to work

Friday, September 17th, 2010

reference: http://code.google.com/p/virtualhost-sh/issues/detail?id=9

when you uncomment v-hosts in httpd.conf, they won’t work until you fix that last line…

Mac OSX remove MySQL

Sunday, September 12th, 2010
  1. sudo rm /usr/local/mysql
  2. sudo rm -rf /usr/local/mysql*
  3. sudo rm -rf /Library/StartupItems/MySQLCOM
  4. sudo rm -rf /Library/PreferencePanes/My*
  5. (Edit /etc/hostconfig) sudo vi /etc/hostconfig (Remove line MYSQLCOM=-YES)
  6. sudo rm -rf /Library/Receipts/mysql*
  7. sudo rm -rf /Library/Receipts/MySQL*
  8. sudo rm -rf /var/db/receipts/com.mysql.*

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