jeditable.module in jEditable inline content editing 7
Same filename and directory in other branches
jeditable.module TODO: Provides integration between Drupal and the jEditable jquery plugin @todo: Datepicker support @todo: Ajax upload support @todo: Radioboxes/checkboxes support @todo: add required handler for date @todo: add compatibility for taxonomy_term_reference on select & radios checkboxes
File
jeditable.moduleView source
<?php
/**
* @file jeditable.module
* TODO: Provides integration between Drupal and the jEditable jquery plugin
* @todo: Datepicker support
* @todo: Ajax upload support
* @todo: Radioboxes/checkboxes support
* @todo: add required handler for date
* @todo: add compatibility for taxonomy_term_reference on select & radios checkboxes
*/
/**
* Implements hook_menu().
*/
function jeditable_menu() {
$items['jeditable/ajax/save'] = array(
'title' => 'Save field',
'page callback' => '_jeditable_ajax_save',
'access arguments' => array(
'use jeditable',
),
'type' => MENU_CALLBACK,
);
$items['jeditable/ajax/load'] = array(
'title' => 'Load field',
'page callback' => '_jeditable_ajax_load',
'page arguments' => array(
3,
4,
5,
),
'access arguments' => array(
'use jeditable',
),
'type' => MENU_CALLBACK,
);
$items['admin/config/content/jeditable'] = array(
'title' => 'jEditable',
'description' => 'Configure the jEditable module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'jeditable_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
function jeditable_admin_settings() {
$form['jeditable_create_new_revisions'] = array(
'#type' => 'checkbox',
'#title' => t('Create Node Revisions'),
'#default_value' => variable_get('jeditable_create_new_revisions', 0),
'#description' => t('If enabled, each time a field is changed a new node revision will be generated. This will generate a very full revision table if jeditable is used extensively, so use with caution'),
);
$form['jeditable_empty_placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Empty field placeholder'),
'#default_value' => variable_get('jeditable_empty_placeholder', '--'),
'#description' => t('Text, that will be shown if field is empty'),
);
return system_settings_form($form);
}
/**
* Implements hook_permission().
*/
function jeditable_permission() {
return array(
'use jeditable' => array(
'title' => t('Use jEditable'),
'description' => t('Use jEditable to edit fields in place.'),
),
);
}
/**
* Implements hook_field_formatter_info(),.
*/
function jeditable_field_formatter_info() {
$all_types = array_keys(field_info_field_types());
return array(
'jeditable_textfield' => array(
'label' => t('jEditable textfield'),
'field types' => array(
'text',
'number_integer',
'number_decimal',
'number_float',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
'empty_text' => '--',
),
),
'jeditable_textarea' => array(
'label' => t('jEditable textarea'),
'field types' => array(
'text_long',
'text_with_summary',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
'empty_text' => '--',
),
),
// add support for 'taxonomy_term_reference'
'jeditable_select' => array(
'label' => t('jEditable selectlist'),
'field types' => array(
'list_boolean',
'list_integer',
'list_float',
'list_text',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
'empty_text' => '--',
),
),
'jeditable_upload' => array(
'label' => t('jEditable upload [not working]'),
'field types' => array(
'image',
'file',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
'empty_text' => '--',
),
),
'jeditable_checkbox_radio' => array(
'label' => t('jEditable checkboxes/radios [not working]'),
'field types' => array(
'list_boolean',
'list_integer',
'list_float',
'list_text',
'taxonomy_term_reference',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
),
),
'jeditable_nodereference' => array(
'label' => t('jEditable node reference [not working]'),
'field types' => array(
'nodereference',
),
),
'jeditable_datetime' => array(
'label' => t('jEditable datetime [not working]'),
'field types' => array(
'datetime',
),
'settings' => array(
'fallback_format' => NULL,
'fallback_settings' => array(),
),
),
);
}
/**
* @todo: field collection delta needs to be fixed --< field_delta line 153
* Implements hook_field_formatter_view().
*/
function jeditable_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, &$items, $display) {
$path = drupal_get_path('module', 'jeditable');
$elements = array();
if (empty($items)) {
$items[0]['value'] = variable_get('jeditable_empty_placeholder', t('- None -'));
}
list($tmp, $widget_type) = explode('_', $display['type']);
switch ($display['type']) {
case 'jeditable_textfield':
case 'jeditable_textarea':
case 'jeditable_select':
case 'jeditable_upload':
case 'jeditable_datetime':
case 'jeditable_checkbox_radio':
foreach ($items as $delta => $item) {
$field_delta = isset($display['views_field']) ? $display['views_field']->options['delta_offset'] + $delta : $delta;
$elements[$delta] = array(
'#markup' => theme('jeditable_formatter_' . $display['type'], array(
'element' => $item,
'field' => $instance,
'entity' => $entity,
'entity_type' => $entity_type,
'widget_type' => $widget_type,
'delta' => $field_delta,
)),
);
}
break;
}
foreach ($items as $delta => $item) {
if (user_access('use jeditable')) {
$elements[$delta]['#attached'] = array(
'js' => array(
$path . '/drupal_jeditable.js',
$path . '/jquery.jeditable.mini.js',
),
'css' => array(
$path . '/jeditable.css',
),
);
}
}
return $elements;
}
/**
* Implements hook_views_api().
*/
function jeditable_views_api() {
return array(
'api' => 2.0,
'path' => drupal_get_path('module', 'jeditable') . '/includes',
);
}
/**
* Implements hook_theme().
*/
function jeditable_theme() {
$theme = array(
'jeditable_formatter_jeditable_textfield' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_textarea' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_select' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_upload' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_datetime' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_checkbox_radio' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'widget_type' => NULL,
'delta' => NULL,
),
),
'jeditable_formatter_jeditable_nodereference' => array(
'arguments' => array(
'element' => NULL,
'field' => NULL,
'entity' => NULL,
'entity_type' => NULL,
'delta' => NULL,
),
),
'jeditable_workflow' => array(
'arguments' => array(
'node' => NULL,
),
),
);
return $theme;
}
/**
* Theme a text field as a jeditable textfield.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_textfield($variables) {
$element = $variables['element'];
$field = $variables['field'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$widget_type = $variables['widget_type'];
$delta = $variables['delta'];
switch ($entity_type) {
case 'node':
// Check user's access to editing this node.
if (!node_access('update', $entity)) {
return $element['value'];
}
$id = $entity->nid;
break;
case 'field_collection_item':
// user's access inherited from node.
$field_name = $entity->field_name;
$hostEntity = entity_load_single('field_collection_item', $entity->item_id)
->hostEntity();
$id = $hostEntity->nid;
// override delta with field_collection's delta
if (isset($hostEntity->{$field_name}[$hostEntity->language])) {
foreach ($hostEntity->{$field_name}[$hostEntity->language] as $key => $val) {
if ($val['value'] == $entity->item_id) {
$delta = $key;
}
}
}
break;
case 'user':
// Check user's access to editing this user.
if (!user_edit_access($entity)) {
return $element['value'];
}
$id = $entity->uid;
break;
}
if (strpos($field['field_name'], 'field_') === 0) {
$entity_type = 'field';
}
return '<span id="' . $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $delta . '" class="jeditable jeditable-' . $widget_type . '">' . $element['value'] . '</span>';
}
/**
* Theme a textarea field as a jeditable textarea.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_textarea($variables) {
$element = $variables['element'];
$field = $variables['field'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$widget_type = $variables['widget_type'];
$delta = $variables['delta'];
switch ($entity_type) {
case 'node':
// Check user's access to editing this node.
if (!node_access('update', $entity)) {
return $element['value'];
}
$id = $entity->nid;
break;
case 'field_collection_item':
// user's access inherited from node.
$field_name = $entity->field_name;
$hostEntity = entity_load_single('field_collection_item', $entity->item_id)
->hostEntity();
$id = $hostEntity->nid;
// override delta with field_collection's delta
foreach ($hostEntity->{$field_name}[$hostEntity->language] as $key => $val) {
if ($val['value'] == $entity->item_id) {
$delta = $key;
}
}
break;
case 'user':
// Check user's access to editing this user.
if (!user_edit_access($entity)) {
return $element['value'];
}
$id = $entity->uid;
break;
}
// if($entity_type == 'node') $entity_type='field';
if (strpos($field['field_name'], 'field_') === 0) {
$entity_type = 'field';
}
return '<span id="' . $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $delta . '" class="jeditable jeditable-' . $widget_type . '">' . $element['value'] . '</span>';
}
/**
* Theme a CCK text field as a jeditable nodereference.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_nodereference($element) {
$id = $element['#node']->nid;
$field = $element['#field_name'];
$node = node_load($element[0]['#item']['nid']);
return '<span id="field-' . $id . '-' . $field . '" class="jeditable-select">' . $node->title . '</span>';
}
/**
* Theme a boolean_list field as a jeditable checkbox or radio button.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_checkbox_radio($variables) {
$element = $variables['element'];
$field = $variables['field'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$delta = $variables['delta'];
$widget_type = $variables['widget_type'];
switch ($entity_type) {
case 'node':
// Check user's access to editing this node.
if (!node_access('update', $entity)) {
return $element['value'];
}
$id = $entity->nid;
break;
case 'user':
// Check user's access to editing this user.
if (!user_edit_access($entity)) {
return $element['value'];
}
$id = $entity->uid;
break;
}
if ($entity_type == 'node') {
$entity_type = 'field';
}
// add check on checkbox or radio
/**
* needs custom ...
* limit = 1 -> radio
* limit >= 1 -> checkbox
* also see ajaxcall class.. adjust
*/
return '<span id="' . $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $variables['delta'] . '" class="jeditable jeditable-' . $widget_type . '">' . $element['value'] . '</span>';
}
/**
* Theme a list field as a jeditable select.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_select($variables) {
$element = $variables['element'];
$field = $variables['field'];
$field_name = $field['field_name'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$widget_type = $variables['widget_type'];
$delta = $variables['delta'];
$field_info = field_info_field($field_name);
//key value instead of key as value
$key_val = isset($field_info['settings']['allowed_values'][$element['value']]) ? $field_info['settings']['allowed_values'][$element['value']] : t('- None -');
switch ($entity_type) {
case 'node':
// Check user's access to editing this node.
if (!node_access('update', $entity)) {
return $element['value'];
}
$id = $entity->nid;
break;
case 'field_collection_item':
// user's access inherited from node.
$field_name = $entity->field_name;
$hostEntity = entity_load_single('field_collection_item', $entity->item_id)
->hostEntity();
$id = $hostEntity->nid;
// override delta with field_collection's delta
foreach ($hostEntity->{$field_name}[$hostEntity->language] as $key => $val) {
if ($val['value'] == $entity->item_id) {
$delta = $key;
}
}
break;
case 'user':
// Check user's access to editing this user.
if (!user_edit_access($entity)) {
return $element['value'];
}
$id = $entity->uid;
break;
}
if (strpos($field['field_name'], 'field_') === 0) {
$entity_type = 'field';
}
return '<span id="' . $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $delta . '" class="jeditable jeditable-' . $widget_type . '">' . $key_val . '</span>';
}
/**
* Theme a CCK text field as a jeditable datepicker.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_datetime($variables) {
$element = $variables['element'];
$field = $variables['field'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$widget_type = $variables['widget_type'];
$delta = $variables['delta'];
if ($entity_type == 'node') {
if (!node_access('update', $entity)) {
return $element['value'];
// make this formatted
}
$id = $entity->nid;
}
if (strpos($field['field_name'], 'field_') === 0) {
$entity_type = 'field';
}
return '<span id="' . $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $variables['delta'] . '" class="jeditable jeditable-' . $widget_type . '">' . $element['value'] . '</span>';
}
/**
* Theme a list field as a jeditable select.
*
* @ingroup themeable
*/
function theme_jeditable_formatter_jeditable_upload($variables) {
$element = $variables['element'];
$field = $variables['field'];
$entity = $variables['entity'];
$entity_type = $variables['entity_type'];
$widget_type = $variables['widget_type'];
$delta = $variables['delta'];
switch ($entity_type) {
case 'node':
// Check user's access to editing this node.
if (!node_access('update', $entity)) {
return $element['value'];
}
$id = $entity->nid;
break;
case 'field_collection_item':
// node_access handled by its node.
$id = entity_load_single('field_collection_item', $entity->item_id)
->hostEntity()->nid;
break;
case 'user':
// Check user's access to editing this user.
if (!user_edit_access($entity)) {
return $element['value'];
}
$id = $entity->uid;
break;
}
if (strpos($field['field_name'], 'field_') === 0) {
$entity_type = 'field';
}
return theme('image', array(
'path' => $element['uri'],
'alt' => $element['alt'],
'title' => $element['title'],
'width' => $element['width'],
'height' => $element['height'],
'attributes' => array(
'class' => 'jeditable jeditable-' . $widget_type,
'id' => $entity_type . '-' . $id . '-' . $field['field_name'] . '-' . $widget_type . '-' . $variables['delta'],
),
));
}
/**
* Theme a workflow state name as a jeditable select list.
*
* @param object $node
* The node object to be displayed
*
* @ingroup themeable
* @return string
*/
function theme_jeditable_workflow($node) {
$id = $node->nid;
// in this case we can use field to store the current workflow id
$field = $node->_workflow ? $node->_workflow : $node->workflow;
// named differently depending on how far the node has loaded
$state = workflow_get_state_name($field);
return '<span id="workflow-' . $id . '-' . $field . '" class="jeditable-select">' . $state . '</span>';
}
/**
* Helper function to save a value using the jeditable callback
*/
function _jeditable_ajax_save() {
// Retrieve the values needed from the post to this page
$array = explode('-', $_POST['id']);
// Assign variables as if they were an array
list($type, $id, $field_name, $field_type, $delta) = $array;
$value = check_plain($_POST['value']);
switch ($type) {
case 'node':
$node = node_load($id);
if (!node_access('update', $node)) {
// check to see that current user has update permissions on the node
$value = 'access denied';
// this is the value that will be returned, but no updates made
}
else {
if ($field_name == 'body') {
$node->body['und'][0]['value'] = $value;
}
else {
$node->{$field_name} = $value;
}
$node->revision = variable_get('jeditable_create_new_revisions', false);
node_save($node);
}
break;
case 'user':
$user = user_load($id);
if (!user_edit_access($user)) {
// check to see that current user has update permissions on the user
$value = 'access denied';
// this is the value that will be returned, but no updates made
}
else {
$field_info = field_info_field($field_name);
$user->{$field_name}[$field_info['translatable'] ? $user->language : LANGUAGE_NONE][0]['value'] = $value;
$edit = array(
$field_name => array(
$field_info['translatable'] ? $user->language : LANGUAGE_NONE => array(
$delta => array(
'value' => $value,
),
),
),
);
user_save($user, $edit);
}
break;
case 'field':
$node = node_load($id);
$delta = intval($delta);
if (!node_access('update', $node)) {
// check to see that current user has update permissions on the node
$value = 'access denied';
// this is the value that will be returned, but no updates made
}
else {
$field = field_info_field($field_name);
// get select, radio & checkbox key values
$key_value = $field['settings']['allowed_values'][$value];
$lang = $field['translatable'] ? $node->language : LANGUAGE_NONE;
// assign nid if nodereference, format date if date, otherwise just assign value
if ($field['type'] == 'nodereference') {
$node->{$field_name}[$lang][$delta]['nid'] = $value;
$referenced = node_load($value);
$value = $referenced->title;
}
elseif ($field['type'] == 'datetime') {
$unixtime = strtotime($value);
$value = date('o-m-d H:i:s', $unixtime);
$node->{$field_name}[$lang][$delta]['value'] = $value;
}
else {
//if type = node
if (isset($field['bundles']['node'])) {
$node->{$field_name}[$lang][$delta]['value'] = $value;
}
//if type = field_collection
if (isset($field['bundles']['field_collection_item'])) {
// get field_collection
$field_collection_name = $field['bundles']['field_collection_item'][0];
$field_collection_id = $node->{$field_collection_name}[$lang][$delta]['value'];
$field_collection = entity_load_single('field_collection_item', array(
$field_collection_id,
));
$field_collection->{$field_name}[$lang][0]['value'] = $value;
}
}
$node->revision = variable_get('jeditable_create_new_revisions', false);
node_save($node);
if (isset($field_collection)) {
$field_collection
->save();
}
}
break;
case 'workflow':
$node = node_load($id);
$value = _jeditable_workflow_save($node, $value);
break;
}
if ($field['type'] == 'list_text' || $field['type'] == 'list_boolean') {
$value = $key_value;
}
print $value;
exit;
}
/**
* Helper function to load a list of select values
*/
function _jeditable_ajax_load() {
// Retrieve the values needed from the post to this page
$array = explode('-', $_GET['id']);
// Assign variables as if they were an array
list($type, $id, $field_name, $field_type) = $array;
switch ($type) {
case 'node':
/** Not Implemented yet. This is a test case scenario for editing things such as a Node title **/
$node = node_load($id);
$value = $node->{$field};
$value = 'Y';
$defaults = array(
'E' => 'Letter E',
'M' => 'Letter M',
'Y' => 'Letter Y',
);
$defaults['selected'] = $value;
break;
case 'cck':
/** Right now this supports nodereference only. The same handler should support optionwidget cck types
* in a dropdown, but that needs to be sorted out still.
**/
$node = node_load($id);
$current_value = $node->{$field_name}[0]['nid'];
$field = content_fields($field_name, $node->type);
$values = _nodereference_potential_references($field);
$defaults = array();
foreach ($values as $key => $value) {
$defaults[$key] = $value['rendered'];
}
$defaults['selected'] = $current_value;
break;
case 'field':
$node = node_load($id);
if (!node_access('update', $node)) {
// check to see that current user has update permissions on the node
$value = 'access denied';
// this is the value that will be returned, but no updates made
}
else {
$field = field_info_field($field_name);
$field_info = field_info_instance($entity_type = 'node', $field_name, $node->type);
switch ($field_type) {
// jEditable select
case 'select':
//select load values works for both nodes & field_collections
$defaults = $field['settings']['allowed_values'];
$defaults = $field_info['required'] != 1 ? array(
'_none' => t('- None -'),
) + $defaults : $defaults;
break;
// jEditable radio
case 'radio':
break;
case 'datetime':
break;
}
}
break;
case 'workflow':
/** Load the workflow states available to the current user **/
$node = node_load($id);
$defaults = _jeditable_workflow_load($node, $field_name);
}
print json_encode($defaults);
exit;
}
/**
* Workflow module integration
*
* Workflow API functions needed:
* workflow_execute_transition($node, $sid, $comment = NULL, $force = FALSE); // transition a node
* workflow_get_state_name($sid); // get a state name from a state id
* workflow_field_choices($node); // get the workflow selections available to the current user
* workflow_node_current_state($node); // get the current state of a node
*/
/**
* Load the defaults array for a workflow select
*
* @param object $node
* Node object to get workflow states for.
* @param int $sid
* The state id of the current state.
* @return array
* An array of sid => name, including selected for current selection.
*/
function _jeditable_workflow_load($node, $sid) {
$defaults = workflow_field_choices($node);
// set selected value
$defaults['selected'] = $sid;
return $defaults;
}
/**
* Set the workflow state of the node
*
* @param object $node
* Node to be changed.
* @return string
* the status name after state change.
*/
function _jeditable_workflow_save($node, $sid) {
if ($sid == $node->_workflow) {
// this means there's nothing to change, so we just return the title
return workflow_get_state_name($sid);
}
else {
// here's where we do the actual transition. It will fail if user does not have appropriate permissions.
$new_sid = workflow_execute_transition($node, $sid, 'set using jeditable at ' . $_SERVER['HTTP_REFERER']);
}
if (empty($new_sid)) {
// in this case, the transition failed, so we'll return "access denied"
return "access denied";
}
// finally, this is the intended outcome and we can return the changed state's name
return workflow_get_state_name($new_sid);
}
Functions
Name | Description |
---|---|
jeditable_admin_settings | |
jeditable_field_formatter_info | Implements hook_field_formatter_info(),. |
jeditable_field_formatter_view | @todo: field collection delta needs to be fixed --< field_delta line 153 Implements hook_field_formatter_view(). |
jeditable_menu | Implements hook_menu(). |
jeditable_permission | Implements hook_permission(). |
jeditable_theme | Implements hook_theme(). |
jeditable_views_api | Implements hook_views_api(). |
theme_jeditable_formatter_jeditable_checkbox_radio | Theme a boolean_list field as a jeditable checkbox or radio button. |
theme_jeditable_formatter_jeditable_datetime | Theme a CCK text field as a jeditable datepicker. |
theme_jeditable_formatter_jeditable_nodereference | Theme a CCK text field as a jeditable nodereference. |
theme_jeditable_formatter_jeditable_select | Theme a list field as a jeditable select. |
theme_jeditable_formatter_jeditable_textarea | Theme a textarea field as a jeditable textarea. |
theme_jeditable_formatter_jeditable_textfield | Theme a text field as a jeditable textfield. |
theme_jeditable_formatter_jeditable_upload | Theme a list field as a jeditable select. |
theme_jeditable_workflow | Theme a workflow state name as a jeditable select list. |
_jeditable_ajax_load | Helper function to load a list of select values |
_jeditable_ajax_save | Helper function to save a value using the jeditable callback |
_jeditable_workflow_load | Load the defaults array for a workflow select |
_jeditable_workflow_save | Set the workflow state of the node |