homebox.module in Homebox 6.2
Same filename and directory in other branches
Homebox main file, takes care of global functions settings constants, etc.
Defines menu elements, callbacks, permissions, hooks, preprocess functions, utility functions
File
homebox.moduleView source
<?php
/**
* @file
* Homebox main file, takes care of global functions settings constants, etc.
*
* Defines menu elements, callbacks, permissions, hooks,
* preprocess functions, utility functions
*/
// Sets the number of colors that an administrator can set
define('HOMEBOX_NUMBER_OF_COLOURS', 6);
// Default number of regions for new pages
define('HOMEBOX_DEFAULT_REGIONS', 3);
// The version of Homebox
define('HOMEBOX_VERSION', 2);
// The version of jQuery UI we need
define('HOMEBOX_JQUERY_UI_VERSION', 1.6);
/**
* Implementation of hook_menu().
*/
function homebox_menu() {
$items = array();
// Created Homebox pages
$pages = homebox_pages();
if (is_array($pages) && count($pages) > 0) {
foreach ($pages as $page) {
$items[$page->settings['path']] = array(
'title' => $page->settings['title'],
'page callback' => 'homebox_build',
'page arguments' => array(
$page,
),
'access callback' => '_homebox_user_access_view_homebox',
'access arguments' => array(
$page,
),
'type' => $page->settings['menu'] ? MENU_NORMAL_ITEM : MENU_CALLBACK,
);
$items[$page->settings['path'] . '/block/%'] = array(
'title' => $page->settings['title'],
'page callback' => 'homebox_build_block',
'page arguments' => array(
$page,
count(explode('/', $page->settings['path'])) + 1,
),
'access callback' => '_homebox_user_access_view_homebox',
'access arguments' => array(
$page,
),
'type' => $page->settings['menu'] ? MENU_NORMAL_ITEM : MENU_CALLBACK,
);
}
}
// Optionally add a tab to user profiles
if ($name = variable_get('homebox_user_tab', '')) {
$page = homebox_get_page($name);
if ($page) {
$items['user/%user/' . $page->settings['path']] = array(
'title' => $page->settings['title'],
'page callback' => 'homebox_pre_build_user',
'page arguments' => array(
$page,
1,
),
'access callback' => '_homebox_user_access_view_user_homebox',
'access arguments' => array(
$page,
1,
),
'weight' => 1,
'type' => MENU_LOCAL_TASK,
);
$items['user/%user/' . $page->settings['path'] . '/block/%'] = array(
'page callback' => 'homebox_build_block',
'page arguments' => array(
$page,
4,
),
'access callback' => '_homebox_user_access_view_user_homebox',
'access arguments' => array(
$page,
1,
),
'type' => MENU_CALLBACK,
);
}
}
// Ajax Callbacks
$items['homebox/%homebox/restore'] = array(
'title' => t('Restore'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_restore_defaults',
1,
),
'access callback' => '_homebox_user_access_view_homebox',
'access arguments' => array(
1,
TRUE,
),
'type' => MENU_CALLBACK,
);
$items['homebox/js/%homebox/add/%/%'] = array(
'page callback' => 'homebox_add_block',
'page arguments' => array(
2,
4,
5,
),
'access callback' => 'homebox_edit_access',
'access arguments' => array(
2,
),
'type' => MENU_CALLBACK,
);
$items['homebox/js/%homebox/save'] = array(
'page callback' => 'homebox_js_save_user_settings',
'page arguments' => array(
2,
),
'access callback' => '_homebox_user_access_view_homebox',
'access arguments' => array(
2,
TRUE,
),
'type' => MENU_CALLBACK,
);
// Admin related tasks
$items['admin/build/homebox'] = array(
'title' => 'Homebox',
'description' => 'List, edit, or add homebox pages.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_admin_new_page',
),
'access arguments' => array(
'administer homebox',
),
'file' => 'homebox.admin.inc',
);
$items['admin/build/homebox/edit/%homebox'] = array(
'title' => 'Edit page',
'page arguments' => array(
'homebox_admin_page',
4,
),
'access arguments' => array(
'administer homebox',
),
'type' => MENU_CALLBACK,
'file' => 'homebox.admin.inc',
);
$items['admin/build/homebox/layout/%homebox'] = array(
'title' => 'Layout',
'description' => 'Edit layout.',
'page callback' => 'homebox_layout',
'page arguments' => array(
4,
),
'access arguments' => array(
'administer homebox',
),
'file' => 'homebox.admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/build/homebox/flush/%homebox'] = array(
'title' => 'Flush user settings',
'description' => 'Clear all user\'s settings for a given homebox.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_admin_flush_form',
4,
),
'access arguments' => array(
'administer homebox',
),
'file' => 'homebox.admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/build/homebox/settings/%homebox'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_configure_form',
4,
),
'access arguments' => array(
'administer homebox',
),
'type' => MENU_CALLBACK,
'file' => 'homebox.admin.inc',
);
$items['admin/build/homebox/export/%homebox'] = array(
'title' => 'Export',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_export_form',
4,
),
'access arguments' => array(
'administer homebox',
),
'type' => MENU_CALLBACK,
'file' => 'homebox.admin.inc',
);
$items['admin/build/homebox/delete/%homebox'] = array(
'title' => 'Delete page',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_admin_page_delete_confirm',
4,
),
'access arguments' => array(
'administer homebox',
),
'type' => MENU_CALLBACK,
'file' => 'homebox.admin.inc',
);
// Admin user settings
$items['admin/user/homebox'] = array(
'title' => 'User profile Homebox',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'homebox_user_settings_page',
),
'access arguments' => array(
'administer homebox',
),
'description' => 'Configure the intergration of Homebox and user profiles.',
'file' => 'homebox.admin.inc',
);
return $items;
}
/**
* Implementation of hook_load()
*/
function homebox_load($name) {
$page = homebox_get_page($name);
return $page ? $page : FALSE;
}
/**
* Implementation of hook_forms().
*/
function homebox_forms($form_id, $args) {
switch ($form_id) {
case 'homebox_admin_new_page':
return array(
'homebox_admin_new_page' => array(
'callback' => 'homebox_admin_page',
),
);
case isset($args[1]) && is_object($args[1]) ? 'homebox_block_edit_' . $args[1]->module . '_' . $args[1]->delta . '_form' : NULL:
return array(
$form_id => array(
'callback' => 'homebox_block_edit_form_builder',
),
);
}
}
/**
* Implementation of hook_help().
*/
function homebox_help($path, $arg) {
switch ($path) {
case 'admin/build/homebox':
return '<p>' . theme('advanced_help_topic', 'homebox', 'introduction') . ' ' . t("Homebox pages are listed below. Each page is accessible from a single url on your site that you specify during page creation. You can create as many pages as you need. Be sure to review all available layout options and settings.</p>");
case 'admin/build/homebox/layout/%':
return '<p>' . theme('advanced_help_topic', 'homebox', 'default-layout') . ' ' . t("This page behave the same way as Drupal block administration page. Drag blocks to whatever column you want to enable it for your users. Note that you can change the number of columns in the <a href='!settings_url'>settings page</a>", array(
'!settings_url' => url('admin/build/homebox/settings/' . arg(4)),
)) . '.</p>';
case 'admin/build/homebox/settings/%':
return '<p>' . theme('advanced_help_topic', 'homebox', 'settings') . ' ' . t('Homebox configuration page.');
}
}
/**
* Implementation of hook_perm().
*/
function homebox_perm() {
return array(
'administer homebox',
);
}
/**
* Implementation of hook_theme().
*/
function homebox_theme($blocks) {
return array(
// Set hook name: see template_preprocess_homebox()
'homebox' => array(
'arguments' => array(
'regions' => NULL,
'column_count' => NULL,
'add_links' => NULL,
'save_form' => NULL,
'page' => NULL,
),
'template' => 'homebox',
),
'homebox_block' => array(
'arguments' => array(
'block' => NULL,
'page' => NULL,
),
'template' => 'homebox-block',
),
'homebox_admin_display_form' => array(
'template' => 'homebox-admin-display-form',
'file' => 'homebox.admin.inc',
'arguments' => array(
'form' => NULL,
),
),
'homebox_admin_new_page' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'homebox.admin.inc',
),
);
}
/**
* Preprocesses variables for home-box.tpl.php template
*
* @param $variables
* An array containing variables to used in home-box.tpl.php
* @return
* An array containing preprocessed variables (see home-box.tpl.php)
*/
function template_preprocess_homebox(&$variables) {
// Add required jQuery UI files
jquery_ui_add(array(
'ui.draggable',
'ui.droppable',
'ui.sortable',
));
// Add Homebox JavaScript files
drupal_add_js(drupal_get_path('module', 'homebox') . '/homebox.js', 'module', 'header', FALSE, TRUE, TRUE);
drupal_add_js(drupal_get_path('module', 'homebox') . '/includes/tipsy/jquery.tipsy.js', 'module', 'header', FALSE, TRUE, TRUE);
// Add CSS for homebox
drupal_add_css(drupal_get_path('module', 'homebox') . '/homebox.css', 'module', 'all', TRUE);
drupal_add_css(drupal_get_path('module', 'homebox') . '/includes/tipsy/tipsy.css', 'module', 'all', TRUE);
$classes = array(
'column-count-' . $variables['column_count'],
'homebox-' . $variables['page']->name,
);
if ($variables['page']->settings['auto_save']) {
$classes[] = 'homebox-auto-save';
}
$variables['classes'] = implode(' ', $classes);
return $variables;
}
/**
* Return the homebox page path. If it is a tab on the user page, the path is
* different.
*
* @param $page
* A homebox page object.
* @return
* The string path.
*/
function homebox_get_path($page) {
global $user;
if ($user->uid && $page->name === variable_get('homebox_user_tab', '')) {
return 'user/' . $user->uid . '/' . $page->settings['path'];
}
return $page->settings['path'];
}
/**
* Responsible for firing the hook_theme()
*
* @param $page
* A page object
* @return
* homebox_theme() call
*/
function homebox_build($page) {
global $user;
// If no default block layout is set, return a simple message
if (empty($page->settings['blocks'])) {
return t('This page has not yet been configured.');
}
// Get every block placed into its region sorted by weight
$column_count = $page->settings['regions'];
$regions = array_fill(1, $column_count, array());
// Extract blocks from the page
$blocks = $page->settings['blocks'];
$allowed_blocks = array();
$info = array();
foreach ($page->settings['blocks'] as $block) {
if (!isset($info[$block['module']])) {
$info[$block['module']] = module_invoke($block['module'], 'block', 'list');
}
$allowed_blocks[$block['module']][$block['delta']] = $info[$block['module']][$block['delta']];
if (!empty($block['title'])) {
$allowed_blocks[$block['module']][$block['delta']]['info'] = $block['title'];
}
}
// Get user settings, so custom blocks are placed in regions.
$user_blocks = _homebox_get_user_settings($page);
if ($user_blocks !== FALSE) {
// Add custom blocks.
foreach ($user_blocks as $key => $block) {
if (isset($allowed_blocks[$block['module']][$block['delta']])) {
$blocks[$key] = $user_blocks[$key];
}
}
}
// Preparing blocks object for theming
foreach ($blocks as $key => $block_settings) {
// Adds block to its regions
if ($block_settings['status']) {
$block = homebox_prepare_block($key, $page);
if (!is_null($block)) {
// If user defined region is greater than real column count put block in
// the last column/region.
$regions[min($block->region, $column_count)][$block->weight][] = $block;
$allowed_blocks[$block->module][$block->delta]['used'] = TRUE;
}
}
}
// Sort each region/column based on key value
for ($i = 1; $i <= count($regions); $i++) {
ksort($regions[$i]);
}
// Add block links
if ($user->uid) {
$add_links = array();
foreach ($allowed_blocks as $module => $blocks) {
foreach ($blocks as $delta => $info) {
$options = array();
if (isset($info['used'])) {
$options['attributes'] = array(
'class' => 'used',
);
}
$add_links[] = homebox_add_link($info['info'], $page, $module, $delta, $options);
}
}
$add_links['restore'] = l(t('Restore to defaults'), 'homebox/' . $page->name . '/restore', array(
'attributes' => array(
'class' => 'restore',
),
));
$add_links = theme('item_list', $add_links, NULL, 'ul', array(
'class' => 'clear-block',
));
$save_form = drupal_get_form('homebox_save_form', $page);
}
else {
$add_links = NULL;
$save_form = NULL;
}
// Build output
$output = theme('homebox', $regions, $page->settings['regions'], $add_links, $save_form, $page);
// Build the page
if ($page->settings['full']) {
// If page is set to full, avoid printing other theme block regions
print theme('page', $output, FALSE);
}
else {
// Simply return the page
return $output;
}
}
/**
* Construct a link to add a block to a user dashboard.
*
* @param $text
* Link text.
* @param $page
* Homebox page argument.
* @param $module
* Module of the block to add.
* @param $delta
* $delta of the block to add.
* @param $options
* $options as you would pass to l(). May include query arguments for
* block-specific config.
*
* @return
* An HTML link.
*/
function homebox_add_link($text, $page, $module, $delta, $options = array()) {
$options['query']['token'] = homebox_get_token($page);
return l($text, 'homebox/js/' . $page->name . '/add/' . $module . '/' . $delta, $options);
}
function homebox_save_form($form_state, $page) {
$form = array();
$form['blocks'] = array(
'#type' => 'hidden',
);
$form['messages'] = array(
'#value' => '<span id="homebox-minimize-to-save" class="homebox-msg">' . t('Minimize to save') . '</span>' . '<span id="homebox-changes-made" class="homebox-msg">' . t('Unsaved') . '</span>',
);
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#ahah' => array(
'path' => 'homebox/js/' . $page->name . '/save',
'event' => 'click',
),
);
return $form;
}
function homebox_save_form_submit($form, $form_state) {
parse_str($form_state['values']['blocks'], $blocks);
foreach ($blocks as $key => $value) {
parse_str($value, $block);
$blocks[$key] = $block;
}
_homebox_save_user_settings($form['#parameters'][2], $blocks);
}
/**
* Helper function for performing operations prior to rendering
* a Homebox that is set as a user profile tab
*
* @param $page
* A page object
* @param $user
* A user object
*/
function homebox_pre_build_user($page, $user) {
drupal_set_title($user->name);
$output = homebox_build($page);
// Only return if output is available
// Certain Homeboxes print instead of return
if ($output) {
return $output;
}
}
/**
* Prepare a block for rendering with theme('homebox_block').
*
* @param $block_key
* A string identifying the block. Should match an array key in
* $page->settings['blocks'], except for a user's custom block.
* @param $page
* A homebox page object, as loaded by homebox_get_page().
*
* @return
* A block object that can be passed to theme('homebox_block', $block).
*/
function homebox_prepare_block($block_key, $page) {
global $user;
// Load block settings.
$user_settings = _homebox_get_user_settings($page);
if ($user_settings !== FALSE && isset($user_settings[$block_key])) {
// Custom blocks only exist in user settings.
$block_settings = $user_settings[$block_key];
}
else {
// Otherwise, start with the page defaults.
$block_settings = $page->settings['blocks'][$block_key];
if ($user_settings !== FALSE && isset($user_settings[$block_key])) {
$block_settings = homebox_merge_settings($block_settings, $user_settings[$block_key]);
}
}
$block_settings['key'] = $block_key;
$block = new stdClass();
// Get the edit form early, in case it changes the block.
if (module_hook($block_settings['module'], 'homebox_block_edit_form')) {
$block->edit_form = drupal_get_form('homebox_block_edit_' . $block_settings['module'] . '_' . $block_settings['delta'] . '_form', $page, (object) $block_settings);
// Prepend messages the form may have generated.
$block->edit_form = theme('status_messages', 'error') . $block->edit_form;
// Apply user settings, they may have changed from the form submission.
if (isset($_POST['form_id']) && ($user_settings = _homebox_get_user_settings($page))) {
$block_settings = homebox_merge_settings($block_settings, $user_settings[$block_key]);
}
}
// Make sure unclosable blocks are not closed.
if (!$block_settings['closable']) {
$block_settings['status'] = TRUE;
}
// Build the $block object.
$block->key = $block_key;
$block->subject = $block_settings['title'];
$block->module = $block_settings['module'];
$block->delta = $block_settings['delta'];
$block->region = (int) $block_settings['region'];
$block->weight = (int) $block_settings['weight'];
$block->status = (bool) $block_settings['status'];
$block->open = (bool) $block_settings['open'];
$block->closable = (bool) $block_settings['closable'];
$block->homebox_classes = _homebox_get_css_classes_for_block($block_settings);
if (module_hook($block->module, 'homebox_block_keys')) {
foreach (module_invoke($block->module, 'homebox_block_keys', $block) as $key) {
$block->{$key} = isset($block_settings[$key]) ? $block_settings[$key] : NULL;
}
}
// Check block permissions
if (!_homebox_can_view_block($block, $user)) {
// Permission denied, skip to the next block
return NULL;
}
// Attempt to find a block in the cache table.
if ((bool) $page->settings['cache'] && !count(module_implements('node_grants')) && $_SERVER['REQUEST_METHOD'] == 'GET' && ($cid = homebox_get_cache_id($page, $block)) && ($cache = cache_get($cid, 'cache_block'))) {
$array = $cache->data;
}
else {
// No cache, fetch the blocks from modules
$array = module_invoke($block->module, 'block', 'view', $block->delta, array(
'homebox' => $block,
));
// Block.module will return 'n/a' if a custom block has been deleted
if ($array['content'] == 'n/a') {
// If this is the case, skip this block
return NULL;
}
if (isset($cid)) {
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
}
}
// Render block content
if (isset($array) && is_array($array)) {
foreach ($array as $k => $v) {
// If block has custom title, leave it
if ($k == 'subject' && !empty($block->subject)) {
continue;
}
$block->{$k} = $v;
}
}
// We don't continue to assign this block since Drupal didn't returned any
// content which could be permissions rules applied by any module.
if (!isset($block->content) || trim($block->content) === '') {
return NULL;
}
// If no title provided we try to get one from blocks table
if (!$block->subject && isset($block->bid)) {
$block->subject = db_result(db_query("SELECT title FROM {blocks} WHERE bid = %d", $block->bid));
}
if (!$block->subject && $block->module == 'views') {
$block->subject = _homebox_get_view_name($block);
}
if (!$block->subject) {
$module_blocks = module_invoke($block->module, 'block', 'list');
$block->subject = $module_blocks[$block->delta]['info'];
}
// Fail safe
if (!$block->subject) {
$block->subject = t('<em>No title defined</em>');
}
return $block;
}
/**
* Get a cache ID suitable for using with homebox.
*
* @param $page
* A homebox page object.
* @param $block
* A homebox block object.
*/
function homebox_get_cache_id($page, $block) {
// Get block info for cache setting
static $infos;
if (!isset($infos[$block->module])) {
$infos[$block->module] = module_invoke($block->module, 'block', 'list');
}
if (isset($infos[$block->module][$block->delta]['cache'])) {
$block->cache = $infos[$block->module][$block->delta]['cache'];
}
else {
$block->cache = BLOCK_CACHE_PER_ROLE;
}
// Cache per-page doesn't do so well when blocks are on homebox pages.
if ($block->cache & BLOCK_CACHE_PER_PAGE) {
$block->cache |= ~BLOCK_CACHE_PER_PAGE;
}
// Use standard block key
if ($cid = _block_get_cache_id($block)) {
// But append unique
if (isset($block->edit_form)) {
global $user;
$cid .= ':' . $user->uid . ':' . $page->name . ':' . $block->key;
}
return $cid;
}
return FALSE;
}
/**
* Get an edit form from the implementing module and add the standard buttons
* and submit handling.
*/
function homebox_block_edit_form_builder(&$form_state, $page, $block) {
$form = module_invoke($block->module, 'homebox_block_edit_form', $block);
$form['#attributes']['class'] = 'clear-block';
$form['save'] = array(
'#type' => 'submit',
'#id' => 'save-' . $block->key,
'#value' => t('Save'),
'#ahah' => array(
'path' => $_GET['q'] . '/block/' . $block->key,
'event' => 'click',
'wrapper' => 'homebox-block-' . $block->key,
'method' => 'replaceWith',
),
);
$form['#submit'][] = 'homebox_block_edit_form_builder_submit';
return $form;
}
/**
* Save settings for the block and render a replacement with the updated settings.
*/
function homebox_block_edit_form_builder_submit($form, &$form_state) {
$page = $form['#parameters'][2];
$block = $form['#parameters'][3];
$user_blocks = _homebox_get_user_settings($page, TRUE);
// Make sure needed keys exist.
$user_blocks[$block->key] = array_merge(array_flip(module_invoke($block->module, 'homebox_block_keys', $block)), $user_blocks[$block->key]);
// Replace user settings with form values, when keys match.
$user_blocks[$block->key] = array_merge($user_blocks[$block->key], array_intersect_key($form_state['values'], $user_blocks[$block->key]));
_homebox_save_user_settings($page, $user_blocks);
$form_state['redirect'] = FALSE;
// Clear block-specific cache.
init_theme();
$block->edit_form = TRUE;
cache_clear_all(homebox_get_cache_id($page, $block), 'cache_block');
}
/**
* Render a single block, for AHAH callbacks.
*/
function homebox_build_block($page, $key) {
drupal_json(array(
'status' => TRUE,
'data' => theme('homebox_block', homebox_prepare_block($key, $page), $page),
));
}
function homebox_block($op, $delta = NULL, $edit = array()) {
switch ($op) {
case 'list':
return array(
'custom' => array(
'info' => t('Homebox custom block'),
'cache' => BLOCK_NO_CACHE,
),
);
case 'view':
switch ($delta) {
case 'custom':
return array(
'subject' => isset($edit['homebox']->title_custom) ? strip_tags($edit['homebox']->title_custom) : t('Custom block'),
'content' => isset($edit['homebox']->content) ? filter_xss_admin(_filter_autop($edit['homebox']->content)) : t('Click the gear icon to edit.'),
);
}
}
}
function homebox_homebox_block_keys() {
return array(
'title_custom',
'content',
);
}
function homebox_homebox_block_edit_form($block) {
$form = array();
$form['title_custom'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#size' => 25,
'#default_value' => isset($block->title_custom) ? $block->title_custom : '',
'#required' => TRUE,
);
$form['content'] = array(
'#type' => 'textarea',
'#title' => t('Content'),
'#default_value' => isset($block->content) ? $block->content : '',
'#required' => TRUE,
);
return $form;
}
/**
* Determine if user has access to view a block
*
* @param $block
* A block object
* @param $user
* Optional user object, use current user if not provided
* @return
* Boolean value of user's access to the block
*/
function _homebox_can_view_block($block, $user = NULL) {
// Use current user if non provided
if (!$user) {
global $user;
}
// Check for roles set at the block level
$allowed_roles = db_query("SELECT rid FROM {blocks_roles} WHERE module = '%s' AND delta = '%s'", $block->module, $block->delta);
// Indicate whether or not role restrictions were set on the block
$role_set = FALSE;
// Iterate all available block roles
while ($role = db_fetch_object($allowed_roles)) {
if (isset($user->roles[$role->rid])) {
return TRUE;
}
$role_set = TRUE;
}
// We checked available roles, and didn't match any
if ($role_set) {
return FALSE;
}
// Block created with Views, check access
if ($block->module == 'views') {
if (!_homebox_check_views_block_access($block)) {
return FALSE;
}
}
// If here, user has access
return TRUE;
}
/**
* Merge default page and user settings
*
* @param $default_blocks
* A multidimensional array representing a page's default blocks
* @param $user_blocks
* A multidimensional array representing a user's default blocks for a page
* @return
* A merged array, allowing user settings to override all default settings availble
* The array will persist all default and user settings that the other array doesn't
* originally have.
*/
function homebox_merge_settings($default_blocks, $user_blocks) {
// Iterate through default blocks, overriding with user settings
foreach ($default_blocks as $k => $v) {
if (!array_key_exists($k, $user_blocks)) {
continue;
}
if (is_array($v) && is_array($user_blocks[$k])) {
$default_blocks[$k] = homebox_merge_settings($v, $user_blocks[$k]);
}
else {
$default_blocks[$k] = $user_blocks[$k];
}
}
// Iterate through user blocks, carrying over any leftovers
foreach ($user_blocks as $k => $v) {
if (array_key_exists($k, $default_blocks)) {
continue;
}
if (is_array($v) && is_array($user_blocks[$k])) {
$default_blocks[$k] = homebox_merge_settings($v, $user_blocks[$k]);
}
else {
$default_blocks[$k] = $user_blocks[$k];
}
}
return $default_blocks;
}
/**
* Helper function to fetch a page from the database or from
* a module implementing hook_homebox()
*
* @param $name
* The machine name of the page
* @return
* A page object, or FALSE is one doesn't exist with given $name
*/
function homebox_get_page($name) {
// Fetch page from db
$page = db_fetch_object(db_query("SELECT * FROM {homebox_pages} WHERE name = '%s'", $name));
if ($page) {
// Unserialize the settings
$page->settings = unserialize($page->settings);
return $page;
}
else {
// If not available, check other modules
$pages = module_invoke_all('homebox');
foreach ($pages as $id => $data) {
// Only match page name
if ($name == $id) {
// Build page object
$page = new stdClass();
$page->name = $id;
$page->settings = $data;
// Check the data before using it
$page = homebox_check_page_object($page);
}
}
return $page ? $page : FALSE;
}
}
/**
* Helper function to save an existing page
*
* @param $page
* A page object
* @param $check
* Whether or not we should check the page data first
* @return
* Boolean status of the operation
*/
function homebox_save_page($page, $check = FALSE) {
if ($page->name && is_array($page->settings)) {
// Should we check the page data?
if ($check && !homebox_check_page_object($page)) {
return FALSE;
}
// Delete page, if it exists
db_query("DELETE FROM {homebox_pages} WHERE name = '%s'", $page->name);
// Save the new/updated page
if (!drupal_write_record('homebox_pages', $page)) {
return FALSE;
}
}
else {
return FALSE;
}
return TRUE;
}
/**
* Helper function to delete a page
*
* @param $name
* The machine name of the page to delete
* @return
* Boolean status of the operation
*/
function homebox_delete_page($name) {
// Delete the page and corresponding user settings
if (db_query("DELETE FROM {homebox_pages} WHERE name = '%s'", $name) && db_query("DELETE FROM {homebox_users} WHERE name = '%s'", $name)) {
return TRUE;
}
return FALSE;
}
/**
* Validation helper function for page name
*
* @param $name
* The name to be tested for the page
* @param $element
* Optional, the form element identifier to throw form errors
* @return
* TRUE if name is valid to use, otherwise, FALSE.
*/
function homebox_check_name($name, $element = NULL) {
// Ensure name fits the rules:
if (preg_match('/[^a-z0-9_]/', $name)) {
if ($element) {
form_set_error($element, t('Machine name must be lowercase alphanumeric or underscores only.'));
}
return FALSE;
}
// Check for name dupes
if (db_result(db_query("SELECT COUNT(*) FROM {homebox_pages} WHERE name = '%s'", $name))) {
if ($element) {
form_set_error($element, t('The page name %name already exists. Please choose another page name.', array(
'%name' => $name,
)));
}
return FALSE;
}
return TRUE;
}
/**
* Validation helper function for page path
*
* @param $path
* The path to be tested for the page
* @param $name
* Optional, the name of the page we're checking
* @param $element
* Optional, the form element identifier to throw form errors
* @return
* TRUE if path is valid to use, otherwise, FALSE.
*/
function homebox_check_path($path, $name = NULL, $element = NULL) {
// Ensure path fits the rules:
if (preg_match('/[^-a-z0-9_\\/]/', $path)) {
if ($element) {
form_set_error($element, t('Path must be lowercase alphanumeric, underscores, dashes, or forward-slashes only.'));
}
return FALSE;
}
// Check path for preceeding or trailing forward slashes
if (substr($path, 0, 1) == '/' || substr($path, strlen($path) - 1, 1) == '/') {
if ($element) {
form_set_error($element, t('Path cannot begin or end with a slash.'));
}
return FALSE;
}
// Check path against existing Homebox paths
$pages = db_query("SELECT * FROM {homebox_pages}");
while ($page = db_fetch_object($pages)) {
$page->settings = unserialize($page->settings);
// If this is the page we're checking, skip it
if ($name && $name == $page->name) {
continue;
}
if ($page->settings['path'] == $path) {
if ($element) {
form_set_error($element, t('The chosen path is already in use.'));
}
return FALSE;
}
}
return TRUE;
}
/**
* Validation helper to check a page object
*
* @param $data
* A page object either in the form of imported PHP code or an actual object
* @param $name
* Optionally specify and override the machine name of the page
* @param $element
* Optionally specify a form element name to be used to throw form errors
* @return
* A complete page object, or FALSE if data was invalid
*/
function homebox_check_page_object($data, $name = NULL, $element = NULL) {
$homebox = '';
if (!is_object($data)) {
// Evaluate the imported object
ob_start();
eval($data);
ob_end_clean();
}
else {
// Not importing - just use $data
$homebox = $data;
}
// Whether or not the import is valid
$status = TRUE;
// Check if data was a valid object
if (!is_object($homebox)) {
$status = FALSE;
}
else {
// Check individual settings
foreach ($homebox->settings as $key => $value) {
switch ($key) {
case 'regions':
// Only allow numbers 1-9
if ($value > 9 || $value < 1) {
$status = FALSE;
break 2;
}
case 'menu':
case 'enabled':
case 'auto_save':
case 'cache':
case 'full':
case 'color':
// Check that the previous are numeric values
if (!is_numeric($value)) {
$status = FALSE;
break 2;
}
break;
case 'title':
// Filter title and make sure it still exists afterwards
$homebox->settings[$key] = filter_xss($value);
if (!$homebox->settings[$key]) {
$status = FALSE;
break 2;
}
break;
case 'path':
if (!homebox_check_path($value, NULL, $element)) {
$status = FALSE;
break 2;
}
break;
case 'colors':
case 'roles':
if (!is_array($value)) {
$status = FALSE;
break 2;
}
break;
case 'blocks':
// Check that at least one block was provided
if (!count($homebox->settings['blocks'])) {
$status = FALSE;
break 2;
}
break;
case 'widths':
if ($homebox->settings['widths']) {
if (!is_array($homebox->settings['widths']) || count($homebox->settings['widths']) > $homebox->settings['regions']) {
$status = FALSE;
break 2;
}
foreach ($homebox->settings['widths'] as $width) {
if (!is_numeric($width) || $width > 100 || $width < 0) {
$status = FALSE;
break 3;
}
}
}
break;
}
}
// If name is explicitly specified, used it
$homebox->name = $name ? $name : $homebox->name;
// Check name
if ($status && $homebox->name && !homebox_check_name($homebox->name, $element)) {
$status = FALSE;
}
// Make sure the required values actually exist
$required_keys = array(
'path',
'blocks',
'title',
'regions',
);
if ($status) {
foreach ($required_keys as $key) {
if (!array_key_exists($key, $homebox->settings)) {
$status = FALSE;
break;
}
}
}
}
// If the import isn't valid, and form element provided,
// then flag a form error
if (!$status && $element) {
form_set_error($element, t('Invalid import data provided.'));
}
return $status ? $homebox : FALSE;
}
/**
* Helper function to determine whether or not a page is living
* in code.
*
* @param $name
* A page name
* @return
* TRUE if the page is living in code regardless of whether or not
* the code made it into the DB because of a save
*/
function homebox_page_is_api($name) {
// Fetch all pages living in code
foreach (module_invoke_all('homebox') as $id => $data) {
if ($name == $id) {
return TRUE;
}
}
return FALSE;
}
/**
* Helper function to properly check access to blocks generated by the Views module
* Coda taken from : function views_block($op = 'view', $delta = 0, $edit = array())
*
* @param $block
* A block object
* @return
* Boolean value of user access to the View
*/
function _homebox_check_views_block_access($block) {
$delta = $block->delta;
// if this is 32, this should be an md5 hash.
if (drupal_strlen($delta) == 32) {
$hashes = variable_get('views_block_hashes', array());
if (!empty($hashes[$delta])) {
$delta = $hashes[$delta];
}
}
// This indicates it's a special one.
if (drupal_substr($delta, 0, 1) == '-') {
list($nothing, $type, $name, $display_id) = explode('-', $delta);
if ($view = views_get_view($name)) {
if ($view
->access($display_id)) {
$view
->destroy();
return TRUE;
}
$view
->destroy();
}
}
list($name, $display_id) = explode('-', $delta);
// Load the view
if ($view = views_get_view($name)) {
if ($view
->access($display_id)) {
$view
->destroy();
return TRUE;
}
$view
->destroy();
}
return FALSE;
}
/**
* Helper function to sort blocks by subject alpha asc
*/
function _homebox_compare_block_subject($a, $b) {
if ($a['subject'] == $b['subject']) {
return 0;
}
return $a['subject'] < $b['subject'] ? -1 : 1;
}
/**
* Get the name of the view based on the block
*
* @param $block
* A block object
* @return
* The name of the view
*/
function _homebox_get_view_name($block) {
$delta = $block->delta;
// if this is 32, this should be an md5 hash.
if (drupal_strlen($delta) == 32) {
$hashes = variable_get('views_block_hashes', array());
if (!empty($hashes[$delta])) {
$delta = $hashes[$delta];
}
}
// This indicates it's a special one.
if (drupal_substr($delta, 0, 1) == '-') {
list($nothing, $type, $name, $display_id) = explode('-', $delta);
if ($view = views_get_view($name)) {
$name = $view->display[$display_id]->display_options['block_description'];
$view
->destroy();
}
}
list($name, $display_id) = explode('-', $delta);
// Load the view
if ($view = views_get_view($name)) {
$name = $view->display[$display_id]->display_options['block_description'];
$view
->destroy();
}
return $name;
}
/**
* Javascript callback
*
* Save a users page settings
*/
function homebox_js_save_user_settings($page) {
drupal_get_form('homebox_save_form', $page);
print drupal_json(array(
'status' => TRUE,
'data' => '',
));
}
/**
* Add a custom block to a user's page.
*/
function homebox_add_block($page, $module, $delta) {
$user_blocks = _homebox_get_user_settings($page, TRUE);
// Build custom block
$block = array(
'module' => $module,
'delta' => $delta,
'title' => '',
'open' => 1,
'color' => 'default',
'status' => 1,
'region' => 1,
'movable' => 1,
'closable' => 1,
);
if (module_hook($block['module'], 'homebox_block_keys')) {
foreach (module_invoke($module, 'homebox_block_keys', (object) $block) as $key) {
if (isset($_REQUEST[$key])) {
$block[$key] = $_REQUEST[$key];
}
}
}
// Add custom block to user's blocks
$key = $block['module'] . '_' . $block['delta'];
$id = 1;
if (isset($user_blocks[$key])) {
while (isset($user_blocks[$key . '-' . $id])) {
$id += 1;
}
$key .= '-' . $id;
}
$user_blocks = array_merge(array(
$key => $block,
), $user_blocks);
_homebox_save_user_settings($page, $user_blocks);
drupal_goto(homebox_get_path($page));
}
/**
* Helper function which adds CSS classes to block, for jQuery to work properly
*
* @param $block
* A block array
* @return
* A string containing CSS classes
*/
function _homebox_get_css_classes_for_block($block) {
$classes = array(
'homebox-portlet',
);
// Is the block movable?
if (!($block['movable'] === 0)) {
$classes[] = 'homebox-draggable';
}
// Adds CSS class for collapsed block
if (!$block['open']) {
$classes[] = 'homebox-portlet-collapsed';
}
// Adds CSS for closed block
if (!$block['status']) {
$classes[] = 'homebox-portlet-closed';
}
// Adds CSS if block is unclosable
if ($block['closable'] === 0) {
$classes[] = 'homebox-unclosable';
}
// Adds color css class
if (isset($block['color']) && $block['color'] != 'default') {
$classes[] = 'homebox-color-' . drupal_strtolower($block['color']);
}
return implode(" ", $classes);
}
/**
* Flush all user settings for a given page
*
* @param $page
* A page object
* @return
* TRUE if the operation was successful, otherwise FALSE
*/
function homebox_flush_settings($page) {
if (db_query("DELETE FROM {homebox_users} WHERE name = '%s'", $page->name)) {
return TRUE;
}
return FALSE;
}
/**
* Retrieve an array of all available pages either in the
* database or by hook_homebox()
*
* @param $api
* Whether or not to invoke other modules for pages
* @return
* An array of page objects
*/
function homebox_pages($api = TRUE) {
$pages = array();
// Fetch all available pages from database
$result = db_query("SELECT * FROM {homebox_pages} ORDER BY name");
while ($page = db_fetch_object($result)) {
$page->settings = unserialize($page->settings);
$pages[] = $page;
}
// Fetch all available pages from API
if ($api) {
$result = module_invoke_all('homebox');
foreach ($result as $name => $data) {
// Build page object
$page = new stdClass();
$page->name = $name;
$page->settings = $data;
// Check the data before using it
if ($page = homebox_check_page_object($page)) {
$pages[] = $page;
}
}
}
return empty($pages) ? NULL : $pages;
}
/**
* Fetch user page settings, if they exist.
*
* @param $page
* A page object
* @param $init
* Make sure the user object is fully initialized, not FALSE. Needed for
* adding/editing a single block.
* @param $_reset
* Reload settings from the database.
*
* @return
* An object representing the user's page settings, or FALSE.
*/
function _homebox_get_user_settings($page, $init = FALSE, $_reset = FALSE) {
global $user;
static $cache = array();
$key = $user->uid . ':' . $page->name;
if ($_reset) {
unset($cache[$key]);
}
if (!isset($cache[$key]) || $init && $cache[$key] === FALSE) {
$settings = db_result(db_query("SELECT settings FROM {homebox_users} WHERE uid = %d AND name = '%s'", $user->uid, $page->name));
$cache[$key] = $settings ? unserialize($settings) : FALSE;
if ($cache[$key] === FALSE && $init) {
_homebox_save_user_settings($page, $page->settings['blocks']);
$cache[$key] = _homebox_get_user_settings($page);
}
}
return $cache[$key];
}
/**
* Save the user's page settings
*
* @param $page
* A page object
* @param $blocks
* An array representing the user's page settings
*/
function _homebox_save_user_settings($page, $blocks) {
// This function is only called after user status
// and perms were checked
global $user;
$user_blocks = _homebox_get_user_settings($page);
$i = 0;
foreach ($blocks as $key => $value) {
// Add weights to blocks
$blocks[$key]['weight'] = ++$i;
// Carry over existing settings
if (isset($page->settings['blocks'][$key])) {
$blocks[$key] = homebox_merge_settings($page->settings['blocks'][$key], $blocks[$key]);
}
if ($user_blocks !== FALSE && isset($user_blocks[$key])) {
$blocks[$key] = homebox_merge_settings($user_blocks[$key], $blocks[$key]);
}
}
if ($user_blocks !== FALSE) {
// Preserve any existing user blocks, which are page blocks, and not already
// saved above. These are disabled page defaults, and need to remain to
// continue overriding the page default.
foreach (array_intersect_key($user_blocks, array_diff_key($page->settings['blocks'], $blocks)) as $key => $value) {
$blocks[$key] = $value;
}
}
// Remove any old settings
db_query("DELETE FROM {homebox_users} WHERE uid = %d AND name = '%s'", $user->uid, $page->name);
// Update settings
$data->uid = $user->uid;
$data->name = $page->name;
$data->settings = $blocks;
drupal_write_record('homebox_users', $data);
_homebox_get_user_settings($page, FALSE, TRUE);
}
/**
* Purge user settings for a given page. Used to restore a page to default
* settings.
*
* @param $page
* A homebox page object.
*/
function homebox_restore_defaults($form_state, $page) {
return confirm_form(array(
'#redirect' => homebox_get_path($page),
), t('Restore %title to defaults?', array(
'%title' => $page->settings['title'],
)), homebox_get_path($page));
}
function homebox_restore_defaults_submit($form, $form_state) {
global $user;
db_query("DELETE FROM {homebox_users} WHERE uid = %d AND name = '%s'", $user->uid, $form['#parameters'][2]->name);
}
/**
* Check that a request is logged-in, has access to view the homebox, and has a
* valid token. Use tokens from homebox_get_token().
*/
function homebox_edit_access($page) {
return _homebox_user_access_view_homebox($page, TRUE) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'homebox-' . $page->name);
}
/**
* Return a token to be used in conjuntion with homebox_edit_access().
*/
function homebox_get_token($page) {
return drupal_get_token('homebox-' . $page->name);
}
/**
* Helper function to check if the current user can view
* a given homebox page
*
* @param $page
* A page object
*/
function _homebox_user_access_view_homebox($page, $require_login = FALSE) {
global $user;
if ($require_login && !$user->uid) {
return FALSE;
}
// Admin is always allowed
if (user_access('administer site configuration') || user_access('administer homebox')) {
return TRUE;
}
elseif (!$page->settings['enabled']) {
return FALSE;
}
elseif ($page->settings['roles']) {
// Iterate each role to look for a match
foreach ($page->settings['roles'] as $role) {
if (array_search($role, $user->roles)) {
return TRUE;
}
}
// No matches on restricted role
return FALSE;
}
else {
// If here, no access
return FALSE;
}
}
/**
* Helper function to check access permissions for user
* profile Homebox
*
* Only show tab if you're viewing your own account
*/
function _homebox_user_access_view_user_homebox($page, $profile) {
global $user;
if ($user->uid == $profile->uid && _homebox_user_access_view_homebox($page)) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Implementation of hook_requirements()
*/
function homebox_requirements($phase) {
$requirements = array();
switch ($phase) {
case 'runtime':
if (variable_get('homebox_version', 0) != HOMEBOX_VERSION) {
$requirements['homebox_upgrade'] = array(
'title' => t('Homebox 2'),
'value' => t('Corrupt'),
'description' => t('There is no upgrade path between Homebox 1.x and 2.x. You must completely uninstall and reinstall Homebox.'),
'severity' => REQUIREMENT_ERROR,
);
}
if (jquery_ui_get_version() != HOMEBOX_JQUERY_UI_VERSION) {
$requirements['homebox_jquery'] = array(
'title' => t('Homebox 2'),
'value' => jquery_ui_get_version(),
'description' => t('Homebox 2 is designed to run with jQuery UI 1.6.'),
'severity' => REQUIREMENT_WARNING,
);
}
case 'install':
$requirements['homebox_php'] = array(
'title' => t('Homebox 2'),
'value' => phpversion(),
'description' => t('Homebox 2 requires a PHP version 5.2 or greater.'),
'severity' => _homebox_check_php() ? REQUIREMENT_OK : REQUIREMENT_ERROR,
);
}
return $requirements;
}
/**
* Check if PHP version is usable. Homebox 2 requires
* PHP of at least 5.2
*
* @return
* TRUE if PHP is usable, otherwise FALSE
*/
function _homebox_check_php() {
$version = explode('.', phpversion());
if ($version[0] >= 5) {
if ($version[0] == 5 && $version[1] < 2) {
return FALSE;
}
return TRUE;
}
return FALSE;
}
/**
* Implementation of hook_features_api().
*/
function homebox_features_api() {
require_once 'homebox.features.inc';
return _homebox_features_api();
}
Functions
Name | Description |
---|---|
homebox_add_block | Add a custom block to a user's page. |
homebox_add_link | Construct a link to add a block to a user dashboard. |
homebox_block | |
homebox_block_edit_form_builder | Get an edit form from the implementing module and add the standard buttons and submit handling. |
homebox_block_edit_form_builder_submit | Save settings for the block and render a replacement with the updated settings. |
homebox_build | Responsible for firing the hook_theme() |
homebox_build_block | Render a single block, for AHAH callbacks. |
homebox_check_name | Validation helper function for page name |
homebox_check_page_object | Validation helper to check a page object |
homebox_check_path | Validation helper function for page path |
homebox_delete_page | Helper function to delete a page |
homebox_edit_access | Check that a request is logged-in, has access to view the homebox, and has a valid token. Use tokens from homebox_get_token(). |
homebox_features_api | Implementation of hook_features_api(). |
homebox_flush_settings | Flush all user settings for a given page |
homebox_forms | Implementation of hook_forms(). |
homebox_get_cache_id | Get a cache ID suitable for using with homebox. |
homebox_get_page | Helper function to fetch a page from the database or from a module implementing hook_homebox() |
homebox_get_path | Return the homebox page path. If it is a tab on the user page, the path is different. |
homebox_get_token | Return a token to be used in conjuntion with homebox_edit_access(). |
homebox_help | Implementation of hook_help(). |
homebox_homebox_block_edit_form | |
homebox_homebox_block_keys | |
homebox_js_save_user_settings | Javascript callback |
homebox_load | Implementation of hook_load() |
homebox_menu | Implementation of hook_menu(). |
homebox_merge_settings | Merge default page and user settings |
homebox_pages | Retrieve an array of all available pages either in the database or by hook_homebox() |
homebox_page_is_api | Helper function to determine whether or not a page is living in code. |
homebox_perm | Implementation of hook_perm(). |
homebox_prepare_block | Prepare a block for rendering with theme('homebox_block'). |
homebox_pre_build_user | Helper function for performing operations prior to rendering a Homebox that is set as a user profile tab |
homebox_requirements | Implementation of hook_requirements() |
homebox_restore_defaults | Purge user settings for a given page. Used to restore a page to default settings. |
homebox_restore_defaults_submit | |
homebox_save_form | |
homebox_save_form_submit | |
homebox_save_page | Helper function to save an existing page |
homebox_theme | Implementation of hook_theme(). |
template_preprocess_homebox | Preprocesses variables for home-box.tpl.php template |
_homebox_can_view_block | Determine if user has access to view a block |
_homebox_check_php | Check if PHP version is usable. Homebox 2 requires PHP of at least 5.2 |
_homebox_check_views_block_access | Helper function to properly check access to blocks generated by the Views module Coda taken from : function views_block($op = 'view', $delta = 0, $edit = array()) |
_homebox_compare_block_subject | Helper function to sort blocks by subject alpha asc |
_homebox_get_css_classes_for_block | Helper function which adds CSS classes to block, for jQuery to work properly |
_homebox_get_user_settings | Fetch user page settings, if they exist. |
_homebox_get_view_name | Get the name of the view based on the block |
_homebox_save_user_settings | Save the user's page settings |
_homebox_user_access_view_homebox | Helper function to check if the current user can view a given homebox page |
_homebox_user_access_view_user_homebox | Helper function to check access permissions for user profile Homebox |