View source
<?php
function subform_field_info() {
return array(
'subform' => array(
'label' => 'Subform',
),
);
}
function subform_field_formatter_info() {
return array(
'default' => array(
'label' => 'Default',
'field types' => array(
'subform',
),
),
);
}
function subform_widget_info() {
return array(
'subform' => array(
'label' => 'Subform',
'field types' => array(
'subform',
),
),
);
}
function subform_widget($op, &$node, &$field, &$items) {
switch ($op) {
case 'form':
$node_property = $field['field_name'] . '_parent_node';
if (isset($node->nid)) {
$parent_node = $node->nid;
}
else {
if (isset($node->{$node_property})) {
$parent_node = $node->{$node_property};
}
else {
$parent_node = 0 - floor(rand());
}
}
$form = array(
$field['field_name'] => array(
$field['field_name'] . '_parent_node' => array(
'#type' => 'hidden',
'#default_value' => $parent_node,
),
'widget' => array(
'#type' => 'subform',
'#title' => $field['widget']['label'],
'#mode' => $field['widget']['children_display_mode'],
'#child_side' => $field['widget']['child_side'],
'#relation_class' => $field['widget']['relation_class'],
'#parent_node' => $parent_node,
'#allow_selection' => $field['widget']['allow_selection'],
'#allow_hoisting' => $field['widget']['allow_hoisting'],
'#selection_query' => $field['widget']['selection_query'],
'#type_name' => $node->type,
'#field_name' => $field['field_name'],
),
),
);
return $form;
case 'submit':
$node_property = $field['field_name'] . '_parent_node';
$node->subforms[$field['field_name']] = $node->{$node_property};
}
}
function subform_form_alter($form_id, &$form) {
if ($form_id == '_content_admin_field') {
if ($form['field_type']['#value'] == 'subform') {
unset($form['field']['required']);
unset($form['field']['multiple']);
$form['field']['required'] = array(
'#type' => 'markup',
'#value' => '<div class="form-item"><b>Required?:</b> Not applicable for subform.</div>',
);
$form['field']['multiple'] = array(
'#type' => 'markup',
'#value' => '<div class="form-item"><b>Multiple?:</b> Not applicable for subform.</div>',
);
}
}
}
function subform_widget_settings($op, $widget) {
switch ($op) {
case 'callbacks':
return array(
'default value' => '_none_',
);
case 'form':
$form = array(
'relation_class' => array(
'#type' => 'select',
'#title' => t('Relation Class'),
'#required' => TRUE,
'#default_value' => isset($widget['relation_class']) ? $widget['relation_class'] : null,
'#options' => relation_class_extended_list(),
'#suffix' => l("Manage Relation Classes", "node/manage/relation_class"),
),
'child_side' => array(
'#type' => 'select',
'#title' => t('Child Node Side'),
'#required' => TRUE,
'#default_value' => isset($widget['child_side']) ? $widget['child_side'] : null,
'#suffix' => "Whatever relation class you have selected, determines what node types can be at the left and right ends of a relation instance based off of that relation class. Subform allows on-the-fly creation and referencing of child nodes, but it must know what *type* of child nodes to create.",
'#options' => array(
"left" => "Left",
"right" => "Right",
),
),
'children_display_mode' => array(
'#type' => 'select',
'#title' => t('Children Display Mode'),
'#required' => TRUE,
'#default_value' => isset($widget['children_display_mode']) ? $widget['children_display_mode'] : "edit",
'#options' => array(
"edit" => "Edit",
"view" => "View",
"hide" => "Hide",
),
),
'allow_hoisting' => array(
'#type' => 'select',
'#title' => t('Allow Hoisting'),
'#required' => TRUE,
'#default_value' => isset($widget['allow_hoisting']) ? $widget['allow_hoisting'] : "no",
'#options' => array(
"yes" => "Yes",
"no" => "No",
),
),
'allow_selection' => array(
'#type' => 'select',
'#title' => t('Allow Selection of Existing Records'),
'#required' => TRUE,
'#default_value' => isset($widget['allow_selection']) ? $widget['allow_selection'] : "yes",
'#options' => array(
"yes" => "Yes",
"no" => "No",
),
),
'selection_query' => array(
'#type' => 'textarea',
'#title' => t('Selection Query'),
'#required' => FALSE,
'#default_value' => isset($widget['selection_query']) ? $widget['selection_query'] : "\n SELECT\n nid, title\n FROM\n {node}\n WHERE\n type = 'SOME CONTENT TYPE'\n ",
'#suffix' => "The results of this query will be displayed as a table except that previously-referenced entries will not be displayed. Be aware that an nid field is required.",
),
);
return $form;
case 'save':
return array(
'relation_class',
'child_side',
'children_display_mode',
'allow_selection',
'allow_hoisting',
'selection_query',
);
}
}
function subform_field_settings($op, &$field) {
switch ($op) {
case 'callbacks':
return array(
'view' => CONTENT_CALLBACK_CUSTOM,
);
case 'database columns':
return array(
'subform' => array(
'type' => 'int',
'not null' => FALSE,
'default' => 0,
),
);
}
}
function subform_field($op, &$node, $field, &$items, $teaser, $page) {
switch ($op) {
case 'view':
$element = array(
'#type' => 'subform',
'#title' => $field['widget']['label'],
'#mode' => 'view',
'#child_side' => $field['widget']['child_side'],
'#relation_class' => $field['widget']['relation_class'],
'#parent_node' => $node->nid,
'#allow_selection' => $field['widget']['allow_selection'],
'#allow_hoisting' => $field['widget']['allow_hoisting'],
'#selection_query' => $field['widget']['selection_query'],
'#type_name' => $node->type,
'#field_name' => $field['field_name'],
);
return theme_subform($element);
}
}
function subform_menu($may_cache) {
$items = array();
content_clear_type_cache();
$operations = array(
"view" => true,
"edit" => true,
"delete" => true,
"hoist" => true,
);
$node_access = array(
"view" => 'view',
"edit" => 'update',
"delete" => 'delete',
);
if ($may_cache) {
foreach (node_get_types() as $type => $name) {
$items[] = array(
'path' => 'subform/add/' . $type,
'title' => t($name),
'access' => node_access('create', $type),
'callback' => '_subform_add',
'callback arguments' => array(
$type,
),
'type' => MENU_CALLBACK,
);
}
}
else {
if (arg(0) == 'subform' && is_numeric(arg(1)) && $operations[arg(2)] && arg(3) == null) {
$node = node_load(arg(1));
if ($node->nid) {
$items[] = array(
'path' => 'subform/' . arg(1) . '/' . arg(2),
'title' => t(arg(2)),
'callback' => '_subform_page',
'access' => node_access($node_access[arg(2)], $node),
'type' => MENU_CALLBACK,
);
}
}
}
if (!$may_cache) {
$directions = array(
"left" => true,
"right" => true,
);
if (arg(0) == 'subform_related' && is_numeric(arg(1)) && $directions[arg(2)]) {
$child_side = arg(2);
$child_node = $child_side . "_node";
$child_node_type = $child_side . "_node_type";
$parent_side = $child_side == "left" ? "right" : "left";
$parent_node = $parent_side . "_node";
$parent_node_type = $parent_side . "_node_type";
if (is_numeric(arg(3)) && arg(4) == 'add' && arg(5) == null) {
$relation_class_node = node_load(arg(1));
$parent_node = arg(3) > 0 ? node_load(arg(3)) : arg(3);
if ($relation_class_node->type == "relation_class" && (is_numeric($parent_node) && $parent_node < 0 || $parent_node->type == $relation_class_node->{$parent_node_type})) {
$items[] = array(
'path' => 'subform_related/' . arg(1) . '/' . arg(2) . '/' . arg(3) . '/add',
'title' => t($name),
'access' => node_access('create', $relation_class_node->{$child_node_type}),
'callback' => '_subform_related_add',
'callback arguments' => array(
$relation_class_node,
$parent_node,
$child_side,
),
'type' => MENU_CALLBACK,
);
}
}
else {
if ($operations[arg(3)]) {
$relation_instance_node = relation_instance_load(arg(1));
if (isset($relation_instance_node->id)) {
$node = node_load($relation_instance_node->{$child_node});
if ($node) {
$items[] = array(
'path' => 'subform_related/' . arg(1) . '/' . arg(2) . '/' . arg(3),
'title' => t(arg(3)),
'callback' => '_subform_related_page',
'callback arguments' => array(
$relation_instance_node,
$node->nid,
),
'access' => arg(3) == "hoist" ? true : node_access($node_access[arg(3)], $node),
'type' => MENU_CALLBACK,
);
}
}
}
}
}
if (arg(0) == 'subform_select' && is_string(arg(1)) && is_string(arg(2)) && is_numeric(arg(3)) && $directions[arg(4)]) {
if ($result = db_fetch_object(db_query("SELECT * FROM {node_field_instance} WHERE type_name = '" . arg(1) . "' AND field_name = '" . arg(2) . "'"))) {
$items[] = array(
'path' => 'subform_select/' . arg(1) . '/' . arg(2) . '/' . arg(3) . '/' . arg(4),
'title' => t("Select"),
'callback' => '_subform_select',
'callback arguments' => array(
$result,
arg(3),
arg(4),
),
'access' => TRUE,
'type' => MENU_CALLBACK,
);
}
}
}
return $items;
}
function _subform($relation_class_id, $child_side, $parent_node_id, $selection_query = null, $allow_selection, $allow_hoisting, $mode = 'hide', $type_name, $field_name) {
$subform_id = $parent_node_id;
$relation_class = node_load($relation_class_id);
$parent_side = $child_side == "left" ? "right" : "left";
$child_side_cardinality = $child_side . "_node_cardinality";
if (is_object($relation_class_id)) {
$relation_class_node = $relation_class_id;
$relation_class_id = $relation_class_node->nid;
}
else {
$relation_class_node = node_load($relation_class_id);
}
drupal_add_css(drupal_get_path('module', 'subform') . '/subform.css');
$relation_instance_results = db_query_range("SELECT id, " . $child_side . "_node as child_node FROM {relation_instance} WHERE relation_class = {$relation_class_node->nid} AND " . $parent_side . "_node = %d", $parent_node_id, 0, $relation_class->{$child_side_cardinality});
$image_path = drupal_get_path('module', 'subform');
$subform_name = "subform{$relation_class_id}";
$subform_name = str_replace("-", "_", $subform_name);
$table_name = "table{$subform_id}";
$table_name = str_replace("-", "_", $table_name);
$content = "\n<table id=\"{$tableName}\" class=\"table\" cellmargin=\"2\"></table>\n\n<script>\n var {$subform_name} = new Subform( {$subform_id}, '" . substr(url("/", null, null, TRUE), 0, -1) . "', '{$child_side}', '{$mode}', {$relation_class_id}, {$parent_node_id}, " . ($allow_selection == "yes" ? "true" : "false") . ", " . $relation_class->{$child_side_cardinality} . ", '{$type_name}', '{$field_name}', '{$image_path}', " . ($allow_hoisting == 'yes' ? 'true' : 'false') . ");\n {$subform_name}.renderLinks();\n {$subform_name}.refresh();\n\n";
while ($relation_instance_result = db_fetch_object($relation_instance_results)) {
$content .= " {$subform_name}.addChild( " . $relation_instance_result->id . " );\n";
}
$content .= "\n</script>";
return $content;
}
function subform_nodeapi(&$node, $op, $a3 = null, $a4 = null) {
if ($op == 'insert' && isset($node->subform_new)) {
drupal_goto("subform/{$node->nid}/edit");
}
if ($op == 'insert' && isset($node->subform_related_new)) {
$child_side = $node->child_side;
$parent_side = $child_side == 'left' ? 'right' : 'left';
$child_side_node = $child_side . '_node';
$parent_side_node = $parent_side . '_node';
$relation_instance = (object) array(
'relation_class' => $node->relation_class,
$parent_side_node => $node->parent_node,
$child_side_node => $node->nid,
);
$relation_instance_id = relation_instance_insert($relation_instance);
drupal_goto("subform_related/{$relation_instance_id}/{$child_side}/edit");
}
if ($op == 'insert' && isset($node->subforms)) {
foreach ($node->subforms as $field => $parent_node) {
if ($parent_node < 0) {
relation_instance_update($parent_node, $node->nid);
}
}
}
}
function _subform_related_add($relation_class_node, $parent_node, $child_side) {
global $user;
$child_side = arg(2);
$child_node_type = $child_side . "_node_type";
$child_node_type = $relation_class_node->{$child_node_type};
$child_node_cardinality = $child_side . "_node_cardinality";
$child_node_cardinality = $relation_class_node->{$child_node_cardinality};
$parent_side = $child_side == "left" ? "right" : "left";
$parent_node_type = $parent_side . "_node_type";
$parent_node_type = $relation_class_node->{$parent_node_type};
$parent_node_cardinality = $parent_side . "_node_cardinality";
$parent_node_cardinality = $relation_class_node->{$parent_node_cardinality};
$content_type = $child_node_type;
drupal_add_js('misc/autocomplete.js');
drupal_add_js('misc/collapse.js');
drupal_add_css(drupal_get_path('module', 'subform') . '/subform.css');
drupal_add_css(drupal_get_path('module', 'content') . '/content.css');
$node = (object) array(
'uid' => $user->uid,
'name' => $user->name,
'type' => $content_type,
);
$form = node_form_array($node);
$form['relation_class'] = array(
'#type' => 'hidden',
'#value' => $relation_class_node->nid,
);
$form['parent_node'] = array(
'#type' => 'hidden',
'#value' => isset($parent_node->nid) ? $parent_node->nid : $parent_node,
);
$form['child_side'] = array(
'#type' => 'hidden',
'#value' => $child_side,
);
$form['#submit']['_subform_related_redirect'] = array();
$form['#submit']['node_form_submit'] = array();
unset($form['preview']);
$content = drupal_get_form($node->type . '_node_form', $form, 'node_form');
$messages = theme_status_messages();
$content = str_replace('<input type="submit" name="op" value="Submit" class="form-submit" />', '<input type="hidden" name="op" id="op" value="Submit">', $content);
$head = drupal_get_html_head();
$styles = theme_get_styles();
include_once 'subform_related.tpl.php';
}
function _subform_add($content_type) {
global $user;
drupal_add_js('misc/autocomplete.js');
drupal_add_js('misc/collapse.js');
drupal_add_css(drupal_get_path('module', 'content') . '/content.css');
$node = (object) array(
'uid' => $user->uid,
'name' => $user->name,
'type' => $content_type,
);
$form = node_form_array($node);
$form['#submit']['_subform_redirect'] = array();
$form['#submit']['node_form_submit'] = array();
unset($form['preview']);
$messages = theme_status_messages();
$content = drupal_get_form($node->type . '_node_form', $form, 'node_form');
$head = drupal_get_html_head();
$styles = theme_get_styles();
include_once 'subform.tpl.php';
}
function _subform_page() {
$node_id = arg(1);
$operation = arg(2);
drupal_add_js('misc/collapse.js');
drupal_add_css(drupal_get_path('module', 'content') . '/content.css');
$node = node_load($node_id);
if ($operation == 'edit') {
if ($_POST['op'] == 'Delete') {
if ($_REQUEST['destination']) {
$destination = drupal_get_destination();
unset($_REQUEST['destination']);
}
drupal_goto('subform/' . arg(1) . '/delete', $destination);
return;
}
$form = node_form_array($node);
unset($form['preview']);
$form['#redirect'] = "subform/{$node_id}/edit";
$content = drupal_get_form($node->type . '_node_form', $form, 'node_form');
$messages = theme_status_messages();
}
else {
if ($operation == 'delete') {
node_delete($node_id);
$content = '';
}
else {
$content = node_view($node);
}
}
$head = drupal_get_html_head();
$styles = theme_get_styles();
include_once 'subform.tpl.php';
}
function subform_delete_relation_instance_submit($form_id, $form_values) {
relation_instance_delete($form_values['relation_instance_id']);
$output = "\n<html>\n<body>\n<script>\nparent.subform.removeChild( " . $form_values['relation_instance_id'] . " );\n</script>\n</body>\n</html>\n";
print $output;
die;
}
function _subform_select_submit($form_id, $form_values) {
$child_side = $form_values['child_side'];
$parent_side = $child_side == 'left' ? 'right' : 'left';
$child_side_node = $child_side . '_node';
$child_side_node_cardinality = $child_side . '_node_cardinality';
$parent_side_node = $parent_side . '_node';
$parent_node = $form_values['parent_node'];
$relation_class = $form_values['relation_class'];
$relation_class_node = node_load($relation_class);
$allowed_slots = $relation_class_node->{$child_side_node_cardinality};
$used_slots = relation_instance_used_slots($relation_class, $parent_side_node, $child_side_node, $parent_node);
if ($form_values['select_list']['rows']) {
foreach ($form_values['select_list']['rows'] as $row_key => $row_value) {
foreach ($row_value as $cell_key => $cell_value) {
if ($cell_value[0] == 1 && $used_slots < $allowed_slots) {
$child_node = $cell_key;
$relation_instance = (object) array(
'relation_class' => $relation_class,
$parent_side_node => $parent_node,
$child_side_node => $child_node,
);
$relation_instance = relation_instance_insert($relation_instance);
$used_slots++;
$relation_instances[] = $relation_instance;
}
}
}
}
$output = '';
if (is_array($relation_instances)) {
$output = implode(",", $relation_instances);
}
print $output;
die;
}
function _subform_select($result, $parent_node, $child_side) {
$result = unserialize($result->widget_settings);
$relation_class = $result['relation_class'];
$relation_class_node = node_load($relation_class);
$limit_query = relation_instance_selectables_query($relation_class, $child_side, $parent_node);
$selection_query = $result['selection_query'];
$selectable_query = "SELECT * FROM ({$limit_query}) as limit_query INNER JOIN ({$selection_query}) AS selection_query ON limit_query.nid = selection_query.nid";
$selectable_results = db_query($selectable_query);
$form = array(
'relation_class' => array(
'#type' => 'hidden',
'#value' => $relation_class,
),
'child_side' => array(
'#type' => 'hidden',
'#value' => $child_side,
),
'parent_node' => array(
'#type' => 'hidden',
'#value' => $parent_node,
),
'select_list' => array(
'#type' => 'fapi_table',
'#tree' => true,
'#width' => "100%",
'rows' => array(),
'#header' => true,
),
'submit' => array(
'#type' => 'submit',
),
'javascript' => array(
'#type' => 'markup',
'#value' => "\n<script>\n\nfunction selectAll(form) {\n for(var i = 0; i < form.elements.length; i++)\n {\n if( form.elements[i].type == 'checkbox' )\n {\n form.elements[i].checked = true;\n }\n }\n}\n\n</script>\n",
),
'select_all' => array(
'#type' => 'markup',
'#value' => "<a href='#' onClick=\"selectAll(document.getElementById('_subform_select'));\" class='subform_button'><img src='/" . drupal_get_path('module', 'subform') . "/accept.png'>Select All</a>",
),
);
$result_found = 0;
while ($selectable_result = db_fetch_array($selectable_results)) {
if (!$form['select_list']['rows']) {
$header_row["header_Select"] = array(
array(
'#type' => 'markup',
'#value' => 'Select',
),
);
foreach ($selectable_result as $key => $value) {
if ($key != 'nid' && $key != 'related_count') {
$header_row["header_{$key}"] = array(
array(
'#type' => 'markup',
'#value' => $key,
),
'#table-sortable' => gettype($value),
'#table-filterable' => true,
);
}
}
$form['select_list']['rows'][] = $header_row;
}
$new_record = array();
$new_record[$selectable_result['nid']] = array(
array(
'#type' => 'checkbox',
'#default_value' => false,
),
);
foreach ($selectable_result as $key => $value) {
if ($key != 'nid' && $key != 'related_count') {
$new_record[] = array(
array(
'#type' => 'markup',
'#value' => $value,
),
);
}
}
$form['select_list']['rows'][] = $new_record;
$result_found++;
}
if ($result_found == 0) {
$new_record[] = array(
array(
'#type' => 'markup',
'#value' => 'No results were found',
),
);
$form['select_list']['rows'][] = $new_record;
unset($form['submit']);
}
drupal_add_css(drupal_get_path('module', 'subform') . '/subform_select.css');
$content = drupal_get_form("_subform_select", $form);
$content = str_replace("<input type=\"submit\" name=\"op\" value=\"\" class=\"form-submit\" />", "<input type='hidden' name='op' id='op' value='Submit'>", $content);
$head = drupal_get_html_head();
$styles = theme_get_styles();
include_once 'subform_related.tpl.php';
die;
}
function _subform_related_page($relation_instance_node, $node_id) {
$relation_instance_id = arg(1);
$operation = arg(3);
$relation_class_node = node_load($relation_instance_node->relation_class);
$child_side = arg(2);
$child_node = $child_side . '_node';
$child_node = $relation_instance_node->{$child_node};
$parent_side = $child_side == 'left' ? 'right' : 'left';
$parent_node = $parent_side . '_node';
$parent_node = $relation_instance_node->{$parent_node};
$form_delete_relation_instance = array(
'relation_instance_id' => array(
'#type' => 'hidden',
'#value' => $relation_instance_id,
),
'submit' => array(
'#type' => 'submit',
),
);
$form_delete_relation_instance = str_replace("<input type=\"submit\" name=\"op\" value=\"\" class=\"form-submit\" />", "<input type='hidden' name='op' id='op' value='Submit'>", drupal_get_form('subform_delete_relation_instance', $form_delete_relation_instance));
$node = node_load($node_id);
if ($operation == 'hoist') {
drupal_goto("node/{$node_id}/edit");
}
else {
if ($operation == 'edit') {
if ($_POST['op'] == 'Delete') {
if ($_REQUEST['destination']) {
$destination = drupal_get_destination();
unset($_REQUEST['destination']);
}
drupal_goto("subform_related/{$relation_instance_id}/{$child_side}/delete", $destination);
return;
}
$form = node_form_array($node);
unset($form['preview']);
unset($form['delete']);
$form['#redirect'] = "subform_related/{$relation_instance_id}/{$child_side}/edit";
$content = $form_delete_relation_instance . drupal_get_form($node->type . '_node_form', $form, 'node_form');
$content = str_replace('<input type="submit" name="op" value="Submit" class="form-submit" />', '<input type="hidden" name="op" id="op" value="Submit">', $content);
$messages = theme_status_messages();
}
else {
if ($operation == 'delete') {
node_delete($node_id);
$content = '';
}
else {
$content = node_view($node);
}
}
}
$head = drupal_get_html_head();
$styles = theme_get_styles();
print $messages . $content;
}
function _subform_redirect($op, &$node) {
drupal_set_message(t('Your %post was created.', array(
'%post' => node_get_types('name', $node),
)));
$node['subform_new'] = TRUE;
}
function _subform_related_redirect($op, &$node) {
drupal_set_message(t('Your %post was created.', array(
'%post' => node_get_types('name', $node),
)));
$node['subform_related_new'] = TRUE;
}
function subform_elements() {
$type['subform'] = array(
'#child_side' => null,
'#relation_class' => null,
'#parent_node' => null,
'#allow_selection' => null,
'#allow_hoisting' => null,
'#selection_query' => null,
);
return $type;
}
function theme_subform($subform_element) {
if (arg(0) == 'subform_related' || arg(0) == 'subform_related') {
return '';
}
if (isset($subform_element['#relation_class']) && isset($subform_element['#child_side']) && isset($subform_element['#parent_node'])) {
drupal_set_html_head(theme('stylesheet_import', base_path() . drupal_get_path('module', 'subform') . '/subform.css'), "module");
drupal_add_js(drupal_get_path('module', 'subform') . '/jquery.js');
drupal_add_js(drupal_get_path('module', 'subform') . '/jquery.form.js');
drupal_add_js(drupal_get_path('module', 'subform') . '/drupal_jquery_liaison.js');
drupal_add_js(drupal_get_path('module', 'subform') . '/subform.js');
drupal_add_js(drupal_get_path('module', 'subform') . '/table.js');
$attributes = drupal_attributes($subform_element['#attributes']);
$title = isset($subform_element['#title']) ? "<label for=\"edit-title\" class=\"subform_label\">" . $subform_element['#title'] . ':</label>' : '';
$subform = _subform($subform_element['#relation_class'], $subform_element['#child_side'], $subform_element['#parent_node'], $subform_element['#selection_query'], $subform_element['#allow_selection'], $subform_element['#allow_hoisting'], $subform_element['#mode'], $subform_element['#type_name'], $subform_element['#field_name']);
$subform = "<div class=\"form-item\">{$title}<div class=\"subform\">{$subform}</div></div>";
return $subform . "\n";
}
}