cpn.module in Code per Node 6
Same filename and directory in other branches
Primary hook implementations.
File
cpn.moduleView source
<?php
/**
* @file
* Primary hook implementations.
*/
/**
* Implementation of hook_perm().
*/
function cpn_perm() {
return array(
'edit css per node',
'edit javascript per node',
'edit css per block',
'edit javascript per block',
);
}
/**
* Implementation of hook_menu().
*/
function cpn_menu() {
$items['admin/settings/cpn'] = array(
'title' => 'Code per Node',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'cpn_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'cpn.admin.inc',
);
return $items;
}
/**
* Implemenation of hook_form_alter().
*/
function cpn_form_alter(&$form, $form_state, $form_id) {
// Node form.
if (isset($form['type']) and isset($form['#node']) and $form_id == $form['type']['#value'] . '_node_form') {
$title = array();
// CSS.
if (variable_get('cpn_css_' . $form['#node']->type, FALSE) and user_access('edit css per node')) {
$form['cpn']['css'] = array(
'#type' => 'textarea',
'#title' => t('CSS'),
'#default_value' => $form['#node']->cpn['css'],
'#description' => t('Custom CSS rules for this node. Do not include @style tags.', array(
'@style' => '<style>',
)),
);
$title[] = 'CSS';
}
// JS.
if (variable_get('cpn_js_' . $form['#node']->type, FALSE) and user_access('edit javascript per node')) {
$form['cpn']['js'] = array(
'#type' => 'textarea',
'#title' => t('JavaScript'),
'#default_value' => $form['#node']->cpn['js'],
'#description' => t('Custom JavaScript for this node. Do not include @script tags.', array(
'@script' => '<script>',
)),
);
$title[] = 'JavaScript';
}
// Fieldset.
if (isset($form['cpn'])) {
$form['cpn']['#type'] = 'fieldset';
$form['cpn']['#title'] = t(join(' & ', $title));
$form['cpn']['#collapsible'] = TRUE;
$form['cpn']['#tree'] = TRUE;
$form['cpn']['#collapsed'] = !drupal_strlen(trim($form['#node']->cpn['css'] . $form['#node']->cpn['js']));
$form['#after_build'][] = 'cpn_after_build';
}
}
// Add content type settings.
if ($form_id == 'node_type_form' and isset($form['#node_type'])) {
$form['cpn'] = array(
'#type' => 'fieldset',
'#title' => t('Code per Node settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['cpn']['cpn_css'] = array(
'#type' => 'textarea',
'#title' => t('CSS'),
'#default_value' => variable_get('cpn_css_' . $form['#node_type']->type, ''),
'#description' => t('Custom CSS rules for this content type. Do not include @style tags.', array(
'@style' => '<style>',
)),
);
$form['cpn']['cpn_js'] = array(
'#type' => 'textarea',
'#title' => t('JavaScript'),
'#default_value' => variable_get('cpn_js_' . $form['#node_type']->type, ''),
'#description' => t('Custom JavaScript for this content type. Do not include @script tags.', array(
'@script' => '<script>',
)),
);
$form['cpn']['cpn_css_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable custom CSS per node.'),
'#return_value' => 1,
'#default_value' => variable_get('cpn_css_enabled_' . $form['#node_type']->type, FALSE),
'#description' => t('Users with the <em>edit node css</em> permission will be able to edit custom CSS rules per node.'),
);
$form['cpn']['cpn_js_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable custom JavaScript per node.'),
'#return_value' => 1,
'#default_value' => variable_get('cpn_js_enabled_' . $form['#node_type']->type, FALSE),
'#description' => t('Users with the <em>edit node javascript</em> permission will be able to edit custom JavaScript per node.'),
);
$form['#validate'][] = 'cpn_node_type_validate';
$form['#submit'][] = 'cpn_node_type_submit';
$form['#after_build'][] = 'cpn_after_build';
}
// Block form (editing any block, or creating a Block module block).
if ($form_id == 'block_admin_configure' or $form_id == 'block_add_block_form' and $form['module']['#value'] == 'block') {
$title = array();
// Load block CSS & JS.
$cpn = array(
'css' => '',
'js' => '',
);
if (!empty($form['delta']['#value'])) {
$cpn = db_fetch_array(db_query("SELECT css, js FROM {blocks} WHERE module = '%s' AND delta = '%s'", $form['module']['#value'], $form['delta']['#value']));
}
// CSS.
if (user_access('edit css per block')) {
$form['cpn']['css'] = array(
'#type' => 'textarea',
'#title' => t('CSS'),
'#default_value' => $cpn['css'],
'#description' => t('Custom CSS rules for this block. Do not include @style tags.', array(
'@style' => '<style>',
)),
);
$title[] = 'CSS';
}
// JS.
if (user_access('edit javascript per block')) {
$form['cpn']['js'] = array(
'#type' => 'textarea',
'#title' => t('JavaScript'),
'#default_value' => $cpn['js'],
'#description' => t('Custom JavaScript for this block. Do not include @script tags.', array(
'@script' => '<script>',
)),
);
$title[] = 'JavaScript';
}
// Fieldset.
if (isset($form['cpn'])) {
$form['cpn']['#type'] = 'fieldset';
$form['cpn']['#title'] = t(join(' & ', $title));
$form['cpn']['#collapsible'] = TRUE;
$form['cpn']['#tree'] = TRUE;
$form['cpn']['#collapsed'] = FALSE;
$form['submit']['#weight'] = 5;
$form['#after_build'][] = 'cpn_after_build';
$form['#validate'][] = 'cpn_block_validate';
$form['#submit'][] = 'cpn_block_submit';
}
}
}
/**
* Implementation of hook_init().
*/
function cpn_init() {
$page_is_admin = arg(0) == 'admin';
foreach (array(
'css',
'js',
) as $type) {
// Only proceed if the 'agree' option was checked and if some code was
// actually saved.
$agree = variable_get('cpn_global_' . $type . '_agree', FALSE);
$code = variable_get('cpn_global_' . $type, '');
if ($agree && !empty($code)) {
// Only proceed if either this is not an admin page or if the 'load on
// admin pages too' option was checked.
$force_on_admin = (bool) variable_get('cpn_global_' . $type . '_admin', FALSE);
if (!$page_is_admin || $force_on_admin) {
$file = file_create_path(variable_get('cpn_path', 'cpn') . '/global.' . $type);
if (!empty($file)) {
if ($type == 'css') {
drupal_add_css($file, 'theme', NULL, variable_get('cpn_aggregation_css', TRUE));
}
else {
drupal_add_js($file, 'theme', 'header', TRUE, variable_get('cpn_aggregation_js', TRUE));
}
}
}
}
}
}
/**
* "#after_build" function which adds syntax highlighting.
* See http://drupal.org/node/322290.
*/
function cpn_after_build($form_element, &$form_state) {
if (variable_get('cpn_syntax_highlighting', 0) === 'codemirror' && ($path = cpn_codemirror())) {
drupal_add_js($path . '/lib/codemirror.js');
drupal_add_css($path . '/lib/codemirror.css');
if (isset($form_element['cpn']['css'])) {
drupal_add_js($path . '/mode/css/css.js');
}
if (isset($form_element['cpn']['js'])) {
drupal_add_js($path . '/mode/javascript/javascript.js');
}
drupal_add_css($path . '/theme/default.css');
drupal_add_js(drupal_get_path('module', 'cpn') . '/cpn.js');
drupal_add_css(drupal_get_path('module', 'cpn') . '/cpn.css');
}
return $form_element;
}
/**
* Node type validation callback.
*
* Ensures no "style" or "script" tags are included.
*/
function cpn_node_type_validate($form, &$form_state) {
if (cpn_validate($form_state['values']['cpn_css'], 'css')) {
form_set_error('cpn_css', t('Do not include @style tags in the CSS.', array(
'@style' => '<style>',
)));
}
if (cpn_validate($form_state['values']['cpn_js'], 'js')) {
form_set_error('cpn_js', t('Do not include @script tags in the JavaScript.', array(
'@script' => '<script>',
)));
}
}
/**
* Node type submit callback.
*/
function cpn_node_type_submit($form, &$form_state) {
// Delete existing files, then save them.
foreach (array(
'css',
'js',
) as $type) {
// Remove the existing file.
cpn_delete_file($form_state['values']['type'] . '.' . $type);
// Wrap the output.
$output = cpn_wrap_output($form_state['values']['cpn_' . $type], 'node', $type);
if (!empty($output)) {
cpn_save_file($output, $form_state['values']['type'] . '.' . $type);
}
}
}
/**
* Implementation of hook_nodeapi().
*/
function cpn_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
static $previous_op = NULL;
switch ($op) {
// Validating: ensure no "style" or "script" tags are included.
case 'validate':
if (isset($node->cpn['css']) and cpn_validate($node->cpn['css'], 'css')) {
form_set_error('cpn][css', t('Do not include @style tags in the CSS.', array(
'@style' => '<style>',
)));
}
if (isset($node->cpn['js']) and cpn_validate($node->cpn['js'], 'js')) {
form_set_error('cpn][js', t('Do not include @script tags in the JavaScript.', array(
'@script' => '<script>',
)));
}
break;
// Updating: delete from DB and file system, and intentionally fallthrough
// to "insert" op below.
case 'update':
if (isset($node->cpn)) {
db_query("DELETE FROM {cpn} WHERE nid = %d", $node->nid);
cpn_delete_file($node->nid . '.css');
cpn_delete_file($node->nid . '.js');
}
// Inserting: save in DB and file system.
case 'insert':
if (isset($node->cpn) and drupal_strlen(trim($node->cpn['css'] . $node->cpn['js']))) {
db_query("INSERT INTO {cpn} VALUES (%d, '%s', '%s')", $node->nid, $node->cpn['css'], $node->cpn['js']);
// Add the global wrapper code.
foreach (array(
'css',
'js',
) as $type) {
$output = cpn_wrap_output($node->cpn[$type], 'node', $type);
if (!empty($output)) {
cpn_save_file($output, $node->nid . '.' . $type);
}
}
}
break;
// Deleting: delete from DB and file system.
case 'delete':
db_query("DELETE FROM {cpn} WHERE nid = %d", $node->nid);
cpn_delete_file($node->nid . '.css');
cpn_delete_file($node->nid . '.js');
break;
// Loading: add "cpn" variable to the node object.
case 'load':
return array(
'cpn' => db_fetch_array(db_query("SELECT css, js FROM {cpn} WHERE nid = %d", $node->nid)),
);
break;
// Viewing & Previewing.
case 'view':
// Attach the content type CSS/JS.
$css = variable_get('cpn_css_' . $node->type, '');
if (!empty($css)) {
$css = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $node->type . '.css');
if (!empty($css)) {
drupal_add_css($css, 'theme', NULL, variable_get('cpn_aggregation_css', TRUE));
}
}
$js = variable_get('cpn_js_' . $node->type, '');
if (!empty($js)) {
$js = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $node->type . '.js');
if (!empty($js)) {
drupal_add_js($js, 'theme', 'header', TRUE, variable_get('cpn_aggregation_js', TRUE));
}
}
// Previewing: add CSS and/or JS to the page, inline.
if ($previous_op == 'validate') {
if (drupal_strlen(trim($node->cpn['css']))) {
// NOTE: This is injected at the top of the page, before all <link> tags.
// It should be injected after the <link> tags.
// Possible in D7: http://drupal.org/node/259368
// Not easily possible in D6: http://drupal.org/node/143209
drupal_set_html_head('<style type="text/css" media="all"> ' . $node->cpn['css'] . ' </style>');
}
if (drupal_strlen(trim($node->cpn['js']))) {
drupal_add_js($node->cpn['js'], 'inline');
}
}
else {
if (!empty($node->cpn['css']) && drupal_strlen(trim($node->cpn['css']))) {
$css = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $node->nid . '.css');
if (!empty($css)) {
drupal_add_css($css, 'theme', NULL, variable_get('cpn_aggregation_css', TRUE));
}
}
if (!empty($node->cpn['js']) && drupal_strlen(trim($node->cpn['js']))) {
$js = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $node->nid . '.js');
if (!empty($js)) {
drupal_add_js($js, 'theme', 'header', TRUE, variable_get('cpn_aggregation_js', TRUE));
}
}
}
break;
}
$previous_op = $op;
}
/**
* Block validation callback.
*/
function cpn_block_validate($form, &$form_state) {
// Ensure no "style" or "script" tags are included.
if (cpn_validate($form_state['values']['cpn']['css'], 'css')) {
form_set_error('cpn][css', t('Do not include @style tags in the CSS.', array(
'@style' => '<style>',
)));
}
if (cpn_validate($form_state['values']['cpn']['js'], 'js')) {
form_set_error('cpn][js', t('Do not include @script tags in the JavaScript.', array(
'@script' => '<script>',
)));
}
}
/**
* Block submit callback.
*/
function cpn_block_submit($form, &$form_state) {
if (isset($form_state['values']['cpn'])) {
$module = $form_state['values']['module'];
$delta = $form_state['values']['delta'];
// "Block" block was just created; get delta from "boxes" table.
if (empty($delta) and $module == 'block') {
$delta = db_result(db_query("SELECT bid FROM {boxes} ORDER BY bid DESC LIMIT 1"));
}
// Save in database.
db_query("UPDATE {blocks} SET css = '%s', js = '%s' WHERE module = '%s' AND delta = '%s'", $form_state['values']['cpn']['css'], $form_state['values']['cpn']['js'], $module, $delta);
foreach (array(
'css',
'js',
) as $type) {
if (!empty($form_state['values']['cpn'][$type])) {
// Delete the existing file.
cpn_delete_file($module . '-' . $delta . '.' . $type);
// Add the global wrapper code.
$output = cpn_wrap_output($form_state['values']['cpn'][$type], 'block', $type);
// Output the file.
if (!empty($output)) {
cpn_save_file($output, $module . '-' . $delta . '.' . $type);
}
}
}
}
}
/**
* Implementation of template_preprocess_block().
*/
function cpn_preprocess_block(&$vars) {
$block = $vars['block'];
if (!empty($block->css)) {
$css = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $block->module . '-' . $block->delta . '.css');
if (!empty($css)) {
drupal_add_css($css, 'theme', NULL, variable_get('cpn_aggregation_css', TRUE));
}
}
if (!empty($block->js)) {
$js = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $block->module . '-' . $block->delta . '.js');
if (!empty($js)) {
drupal_add_js($js, 'theme', 'header', TRUE, variable_get('cpn_aggregation_js', TRUE));
}
}
}
/**
* Validates CSS or JavaScript.
*/
function cpn_validate($data, $type) {
$patterns = array(
'css' => '~<\\s*\\/?\\s*style\\s*.*?>~i',
'js' => '~<\\s*\\/?\\s*script\\s*.*?>~i',
);
return preg_match($patterns[$type], $data);
}
/**
* Saves CSS & JavaScript in the file system (but only if not empty).
*/
function cpn_save_file($data, $filename) {
if (!drupal_strlen(trim($data))) {
return FALSE;
}
$path = file_create_path(variable_get('cpn_path', 'cpn'));
if (empty($path)) {
return FALSE;
}
file_check_directory($path, FILE_CREATE_DIRECTORY) or mkdir($path, 0755, TRUE);
return file_save_data($data, $path . '/' . $filename, FILE_EXISTS_REPLACE);
}
/**
* Deletes CSS & JavaScript from the file system (but only if it exists).
*/
function cpn_delete_file($filename) {
$path = file_create_path(variable_get('cpn_path', 'cpn') . '/' . $filename);
if (file_exists($path)) {
return file_delete($path);
}
return FALSE;
}
/**
* Implementation of hook_content_extra_fields().
*/
function cpn_content_extra_fields($type_name) {
if (variable_get('cpn_css_' . $type_name, FALSE) || variable_get('cpn_js_' . $type_name, FALSE)) {
$extras['cpn'] = array(
'label' => t('Code Per Node'),
'description' => t('Custom CSS and/or JS fields.'),
'weight' => 5,
);
return $extras;
}
}
/**
* Returns path to CodeMirror, or FALSE if not found.
*/
function cpn_codemirror() {
if (module_exists('libraries')) {
$path = libraries_get_path('codemirror');
}
else {
$path = 'sites/all/libraries/codemirror';
}
return file_exists($path) ? $path : FALSE;
}
/**
* Implements hook_ctools_render_alter().
*/
function cpn_ctools_render_alter(&$info, $page, $args, $contexts, $task, $subtask, $handler) {
// Only work with node views.
if ($task['name'] == 'node_view') {
// Get the node data.
if (isset($contexts['argument_nid_1']) && isset($contexts['argument_nid_1']->data)) {
// Pass the node to hook_nodeapi.
cpn_nodeapi($contexts['argument_nid_1']->data, 'view', FALSE, TRUE);
}
}
}
/**
* Wrap output for a given type using the wrapper variables.
*
* @param $code
* The code string to be wrapped.
* @param $entity_type
* The type of object to be wrapped. For now this only supports 'node' and
* 'block'.
* @param $type
* The type of code, i.e. "js" or "css".
*
* @return string
* The wrapped string.
*/
function cpn_wrap_output($code, $entity_type, $type) {
// If the code string is empty, return an empty string.
if (empty($code)) {
return '';
}
// Load the wrapper.
$wrapper = variable_get('cpn_wrapper_' . $entity_type . '_' . $type, '[code]');
// If the wrapper is empty, just return the raw code.
if (empty($wrapper)) {
return $code;
}
// Make the string replacement.
$output = str_replace('[code]', $code, $wrapper);
return $output;
}
Functions
Name | Description |
---|---|
cpn_after_build | "#after_build" function which adds syntax highlighting. See http://drupal.org/node/322290. |
cpn_block_submit | Block submit callback. |
cpn_block_validate | Block validation callback. |
cpn_codemirror | Returns path to CodeMirror, or FALSE if not found. |
cpn_content_extra_fields | Implementation of hook_content_extra_fields(). |
cpn_ctools_render_alter | Implements hook_ctools_render_alter(). |
cpn_delete_file | Deletes CSS & JavaScript from the file system (but only if it exists). |
cpn_form_alter | Implemenation of hook_form_alter(). |
cpn_init | Implementation of hook_init(). |
cpn_menu | Implementation of hook_menu(). |
cpn_nodeapi | Implementation of hook_nodeapi(). |
cpn_node_type_submit | Node type submit callback. |
cpn_node_type_validate | Node type validation callback. |
cpn_perm | Implementation of hook_perm(). |
cpn_preprocess_block | Implementation of template_preprocess_block(). |
cpn_save_file | Saves CSS & JavaScript in the file system (but only if not empty). |
cpn_validate | Validates CSS or JavaScript. |
cpn_wrap_output | Wrap output for a given type using the wrapper variables. |