One Relationship Handler to Rule Them All. (Props to solotandem for the wild idea...
[project/relation.git] / relation_endpoint.module
CommitLineData
8c89308c
KN
1<?php
2
0cbf252c 3/**
4 * @file
5 * Relation endpoints field.
6 */
7
df43f0b1 8/**
9 * Implements hook_field_info().
10 */
d21cffce 11function relation_endpoint_field_info() {
8c89308c
KN
12 return array(
13 'relation_endpoint' => array(
14 'label' => t('Relation endpoint'),
15 'description' => t('This field contains the endpoints of the relation'),
16 'default_widget' => 'relation_endpoint',
17 'default_formatter' => 'relation_endpoint',
18 'entity types' => array('relation'),
a1ea1e19 19 'no_ui' => TRUE,
8c89308c
KN
20 ),
21 );
22}
23
df43f0b1 24/**
25 * Implements hook_field_validate().
26 */
d21cffce 27function relation_endpoint_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
eec23b0d 28 $relation_type = relation_type_load($entity->relation_type);
29 // Check that relation_type exists.
8c89308c 30 if (!$relation_type) {
e385f283 31 $errors[] = array(
32 'error' => 'relation_nonexistent_type',
322db8be 33 'message' => t("The !relation_type relation type does not exist.", array('!relation_type' => $entity->relation_type)),
e385f283 34 );
8c89308c 35 }
a654ce80
KN
36 // Check if the relation type is unique and if so, check if a relation between
37 // those items exist already.
322db8be 38 if ($relation_type->r_unique) {
a654ce80
KN
39 $rids = relation_relation_exists($items, $entity->relation_type);
40 if ($rids && (!isset($entity->rid) || !isset($rids[$entity->rid]))) {
322db8be
KN
41 $errors[] = array(
42 'error' => 'relation_already_exists',
43 'message' => t("The !relation_type is unique but the relation already exists.", array('!relation_type' => $entity->relation_type)),
44 );
45 }
46 }
8c89308c
KN
47 // Check that arity is within acceptable bounds.
48 if (count($items) < $relation_type->min_arity && empty($entity->in_progress)) {
e385f283 49 $errors[] = array(
50 'error' => 'relation_too_few_endpoints',
51 'message' => t("Relation has too few end points (:relation_type min arity :min_arity)", array(':relation_type' => $entity->relation_type, ':min_arity' => $relation_type->min_arity)),
52 );
8c89308c
KN
53 }
54 if ($relation_type->max_arity && count($items) > $relation_type->max_arity) {
e385f283 55 $errors[] = array(
56 'error' => 'relation_too_many_endpoints',
57 'message' => t("Relation has too many end points (:relation_type max arity :max_arity)", array(':relation_type' => $entity->relation_type, ':max_arity' => $relation_type->max_arity)),
58 );
8c89308c
KN
59 }
60 //Check that each entity is has acceptable bundle type and index.
61 foreach ($items as $delta => $item) {
62 $acceptable = FALSE;
63 $directional = $relation_type->directional;
64 $endpoint = ($directional && ($delta > 0)) ? 'target' : 'source';
65 $end_bundles = $endpoint . '_bundles';
66 foreach ($relation_type->$end_bundles as $relation_bundle) {
67 if (!isset($item['entity_bundle'])) {
135f9897
KN
68 $endpoint_entities = entity_load($item['entity_type'], array($item['entity_id']));
69 $endpoint_entity = reset($endpoint_entities);
9c9094a8 70 list(, , $item['entity_bundle']) = entity_extract_ids($item['entity_type'], $endpoint_entity);
8c89308c
KN
71 }
72 $relation_bundle_array = explode(':', $relation_bundle, 2);
73 if (($relation_bundle == $item['entity_type'] . ':' . $item['entity_bundle']) || (($item['entity_type'] == $relation_bundle_array[0]) && ($relation_bundle_array[1] == '*'))) {
74 $acceptable = TRUE;
75 break;
76 }
77 }
78 if (!$acceptable) {
eec23b0d 79 $t_arguments = array('%relation_type' => $entity->relation_type, '@bundle' => $item['entity_bundle']);
8c89308c
KN
80 if ($relation_type->directional) {
81 if ($endpoint == 'target') {
e385f283 82 $errors[] = array(
83 'error' => 'relation_unsupported_target',
84 'message' => t("The %relation_type relation type does not allow @bundle entities as target.", $t_arguments),
85 );
8c89308c
KN
86 }
87 else {
e385f283 88 $errors[] = array(
89 'error' => 'relation_unsupported_source',
90 'message' => t("The %relation_type relation type does not allow @bundle entities as source.", $t_arguments),
91 );
8c89308c
KN
92 }
93 }
94 else {
e385f283 95 $errors[] = array(
96 'error' => 'relation_unsupported_endpoint',
97 'message' => t("The %relation_type relation type does not allow @bundle entities as an endpoint.", $t_arguments),
98 );
8c89308c
KN
99 }
100 }
101 }
102}
103
feb3e358
KN
104/**
105 * Implements hook_field_presave().
106 */
d21cffce 107function relation_endpoint_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
feb3e358 108 // We need r_index here because EntityFieldQuery can't query on deltas.
8c89308c
KN
109 foreach ($items as $delta => &$item) {
110 $item['r_index'] = $delta;
111 }
112}
113
114/**
115 * Implements hook_field_update().
116 */
d21cffce 117function relation_endpoint_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
8c89308c
KN
118 // We do not update.
119}
120
121/**
122 * Implements hook_field_is_empty().
123 */
d21cffce 124function relation_endpoint_field_is_empty($item, $field) {
8c89308c
KN
125 // We are never empty.
126 return FALSE;
127}
128
feb3e358
KN
129/**
130 * Helper to create an HTML table representing a relation.
131 */
d21cffce 132function _relation_endpoint_field_create_html_table($endpoints) {
8c89308c 133 $list_items = array();
feb3e358
KN
134 foreach ($endpoints as $delta => $endpoint) {
135 $entities = entity_load($endpoint['entity_type'], array($endpoint['entity_id']));
8c89308c 136 $entity = reset($entities);
feb3e358
KN
137 $label = entity_label($endpoint['entity_type'], $entity);
138 $uri = entity_uri($endpoint['entity_type'], $entity);
139 $list_items[$delta] = array(l($label, $uri['path'], $uri['options']), $endpoint['entity_type']);
8c89308c 140 }
189bccd7
KN
141 $headers = array(
142 'Entity',
143 array('width' => '22%', 'data' => 'Entity_type'),
144 );
8c89308c
KN
145 return array(
146 '#theme' => 'table',
8c89308c
KN
147 '#header' => $headers,
148 '#rows' => $list_items,
149 );
150}
151
df43f0b1 152/**
edf9822d 153 * Implements hook_field_widget_info().
df43f0b1 154 */
edf9822d 155function relation_endpoint_field_widget_info() {
8c89308c
KN
156 return array(
157 'relation_endpoint' => array(
a1ea1e19
KN
158 'label' => t('Endpoints table'),
159 'field types' => array('relation_endpoint'),
edf9822d
KN
160 'behaviors' => array(
161 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
162 ),
61461a15 163 ),
8c89308c
KN
164 );
165}
166
df43f0b1 167/**
edf9822d 168 * Implements hook_field_formatter_info().
df43f0b1 169 */
edf9822d
KN
170function relation_endpoint_field_formatter_info() {
171 $info = array(
8c89308c 172 'relation_endpoint' => array(
a1ea1e19
KN
173 'label' => t('Endpoints table'),
174 'field types' => array('relation_endpoint'),
edf9822d
KN
175 ),
176 'relation_endpoint_full' => array(
177 'label' => t('Full entities list'),
178 'field types' => array('relation_endpoint'),
e012f5e6 179 ),
8c89308c 180 );
edf9822d
KN
181 foreach (entity_get_info() as $entity_type => $data) {
182 $info['relation_endpoint_full']['settings']['view_modes'][$entity_type] = 'default';
183 }
184 return $info;
185}
186
187/**
188 * Implements hook_field_formatter_settings_form().
189 */
190function relation_endpoint_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
191 $view_modes_settings = $instance['display'][$view_mode]['settings']['view_modes'];
192 foreach (_relation_endpoint_get_endpoint_entity_types($instance) as $endpoint_entity_type => $v) {
193 $entity_info = entity_get_info($endpoint_entity_type);
194 $options = array();
195 foreach ($entity_info['view modes'] as $entity_view_mode => $data) {
196 $options[$entity_view_mode] = $data['label'];
197 }
7c99466d
KN
198 $element['#tree'] = TRUE;
199 $element['view_modes'][$endpoint_entity_type] = array(
edf9822d
KN
200 '#title' => t('@endpoint_entity_type view mode', array('@endpoint_entity_type' => $endpoint_entity_type)),
201 '#type' => 'select',
202 '#default_value' => $view_modes_settings[$endpoint_entity_type],
203 '#options' => $options,
204 );
205 }
206 return $element;
207}
208
209/**
210 * Implements hook_field_formatter_settings_summary().
211 */
212function relation_endpoint_field_formatter_settings_summary($field, $instance, $view_mode) {
213 $view_modes_settings = $instance['display'][$view_mode]['settings']['view_modes'];
214 foreach (_relation_endpoint_get_endpoint_entity_types($instance) as $endpoint_entity_type => $v) {
215 $items[] = "$endpoint_entity_type: " . $view_modes_settings[$endpoint_entity_type];
216 }
217 return theme('item_list', array('items' => $items));
218}
219
220/**
221 * Helper getting endpoint entity types for the bundle specified in $instance.
222 */
223function _relation_endpoint_get_endpoint_entity_types($instance) {
224 $relation_type = relation_type_load($instance['bundle']);
225 $bundles = $relation_type->source_bundles + $relation_type->target_bundles;
226 foreach ($bundles as $bundle_key) {
227 list($endpoint_entity_type) = explode(':', $bundle_key);
228 $endpoint_entity_types[$endpoint_entity_type] = TRUE;
229 }
230 return $endpoint_entity_types;
8c89308c
KN
231}
232
df43f0b1 233/**
234 * Implements hook_field_formatter_view().
235 */
d21cffce 236function relation_endpoint_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
61461a15
KN
237 switch ($display['type']) {
238 case 'relation_endpoint':
02a386ac 239 $build['table'] = _relation_endpoint_field_create_html_table($items);
61461a15 240 case 'relation_endpoint_full':
edf9822d 241 $view_modes_settings = $display['settings']['view_modes'];
61461a15
KN
242 $list_items = array();
243 $endpoint_entity_type = '';
244 $multiple = TRUE;
245 foreach ($items as $delta => $endpoint) {
246 if (!$endpoint_entity_type) {
247 $endpoint_entity_type = $endpoint['entity_type'];
248 }
249 if ($endpoint_entity_type == $endpoint['entity_type']) {
250 $entity_ids[] = $endpoint['entity_id'];
251 }
252 else {
253 $multiple = FALSE;
254 break;
255 }
256 }
61461a15
KN
257 if ($multiple) {
258 $entities = entity_load($endpoint_entity_type, $entity_ids);
edf9822d 259 if (function_exists('entity_view')) {
d155ee9f 260 return array(entity_view($endpoint_entity_type, $entities, $view_modes_settings[$endpoint_entity_type]));
edf9822d
KN
261 }
262 $function = $endpoint_entity_type . '_view_multiple';
61461a15 263 if (function_exists($function)) {
edf9822d 264 return array($function($entities, $view_modes_settings[$endpoint_entity_type]));
61461a15
KN
265 }
266 }
267 $build = array();
268 $function = $endpoint_entity_type . '_view';
269 foreach ($endpoints as $delta => $endpoint) {
270 if ($multiple) {
271 $entity = $entities[$endpoint['entity_id']];
272 }
273 else {
274 $entities = entity_load($endpoint['entity_type'], array($endpoint['entity_id']));
275 $entity = reset($entities);
276 }
edf9822d 277 $build[$delta] = $function($entity, $view_modes_settings[$endpoint_entity_type]);
61461a15 278 }
61461a15 279 }
02a386ac 280 return $build;
8c89308c
KN
281}
282
df43f0b1 283/**
284 * Implements hook_field_widget_form().
285 */
d21cffce 286function relation_endpoint_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
8c89308c
KN
287 foreach ($items as $delta => $item) {
288 foreach (array('entity_type', 'entity_id') as $column) {
289 $element[$delta][$column] = array(
290 '#type' => 'value',
291 '#value' => $item[$column],
292 );
293 }
294 }
d21cffce 295 $element['link_list'] = _relation_endpoint_field_create_html_table($items);
8c89308c
KN
296 return $element;
297}
a1ea1e19
KN
298
299/**
300 * Implements hook_form_field_ui_field_overview_form_alter().
301 */
d21cffce 302function relation_endpoint_form_field_ui_field_overview_form_alter(&$form, $form_state) {
a1ea1e19
KN
303 // Deleting endpoints would make the module useless.
304 if ($form['#entity_type'] == 'relation') {
305 $form['fields']['endpoints']['delete'] = array(
306 '#type' => 'markup',
307 '#markup' => '&nbsp;',
308 );
309 }
310}