| 1 |
<?php
|
| 2 |
// $Id: value_provider.module,v 1.3 2009/01/16 18:30:24 jpetso Exp $
|
| 3 |
/**
|
| 4 |
* @file
|
| 5 |
* Value Providers - Semantic result values for programmatic Views usage.
|
| 6 |
*
|
| 7 |
* Copyright 2008 by Jakob Petsovits ("jpetso", http://drupal.org/user/56020)
|
| 8 |
*/
|
| 9 |
|
| 10 |
include_once(drupal_get_path('module', 'value_provider') . '/value_provider.providers.inc');
|
| 11 |
|
| 12 |
/**
|
| 13 |
* Retrieve a list of value providers, optionally filtered by their value type.
|
| 14 |
*
|
| 15 |
* @param $value_type
|
| 16 |
* If set, only the part of the array containing providers of the given
|
| 17 |
* value type (e.g. 'nid' or 'hours') will be returned. If NULL (default),
|
| 18 |
* the whole structured provider info array will be returned.
|
| 19 |
*
|
| 20 |
* @return
|
| 21 |
* An array in the following format (including some example values):
|
| 22 |
* <code>
|
| 23 |
* array(
|
| 24 |
* 'hours' => array(
|
| 25 |
* 'field_timeneed' => array( // provider name
|
| 26 |
* 'name' => 'field_timeneed', // provider name, too
|
| 27 |
* 'provides' => 'hours', // value type
|
| 28 |
* 'title' => t('Time need field (field_timeneed)'),
|
| 29 |
* 'views table' => $cck_timeneed_views_table_name,
|
| 30 |
* 'columns' => array(
|
| 31 |
* 'iso8601' => $iso8601_real_column_name,
|
| 32 |
* 'seconds' => $seconds_real_column_name,
|
| 33 |
* ),
|
| 34 |
* 'parse callback' => 'mymodule_parse_duration_hours',
|
| 35 |
* 'extra' => $array_with_optional_field_specific_info,
|
| 36 |
* ),
|
| 37 |
* ...
|
| 38 |
* ),
|
| 39 |
* 'nid' => array(
|
| 40 |
* 'field_parent' => array(
|
| 41 |
* 'name' => 'field_parent',
|
| 42 |
* 'provides' => 'nid',
|
| 43 |
* 'title' => t('Parent Node field (field_parent)'),
|
| 44 |
* 'views table' => $cck_timeneed_views_parentnode_name,
|
| 45 |
* 'columns' => array(
|
| 46 |
* 'nid' => $nid_real_column_name,
|
| 47 |
* ),
|
| 48 |
* 'parse callback' => 'mymodule_parse_nodereference_nid',
|
| 49 |
* 'extra' => $array_with_optional_field_specific_info,
|
| 50 |
* ),
|
| 51 |
* ...
|
| 52 |
* ),
|
| 53 |
* ...
|
| 54 |
* )</code>
|
| 55 |
*/
|
| 56 |
function value_providers($value_type = NULL) {
|
| 57 |
static $providers_by_type;
|
| 58 |
if (!isset($providers_by_type)) {
|
| 59 |
views_include_handlers();
|
| 60 |
if (module_exists('content')) {
|
| 61 |
module_load_include('inc', 'content', 'includes/content.views');
|
| 62 |
}
|
| 63 |
|
| 64 |
$providers_by_type = module_invoke_all('value_provider_info');
|
| 65 |
drupal_alter('value_provider_info', $providers_by_type);
|
| 66 |
|
| 67 |
// Insert a few default values.
|
| 68 |
foreach ($providers_by_type as $provider_value_type => $value_providers) {
|
| 69 |
foreach ($value_providers as $provider_name => $provider) {
|
| 70 |
$provider['provides'] = $provider_value_type;
|
| 71 |
$provider['name'] = $provider_name;
|
| 72 |
$providers_by_type[$provider_value_type][$provider_name] = $provider;
|
| 73 |
}
|
| 74 |
}
|
| 75 |
}
|
| 76 |
if (isset($value_type)) {
|
| 77 |
return isset($providers_by_type[$value_type])
|
| 78 |
? $providers_by_type[$value_type]
|
| 79 |
: array();
|
| 80 |
}
|
| 81 |
return $providers_by_type;
|
| 82 |
}
|
| 83 |
|
| 84 |
/**
|
| 85 |
* Retrieve a list of available value providers for the given value type,
|
| 86 |
* compatible for use in a 'select' type form element.
|
| 87 |
*/
|
| 88 |
function value_provider_options($value_type) {
|
| 89 |
$providers = value_providers($value_type);
|
| 90 |
$provider_options = array();
|
| 91 |
|
| 92 |
foreach ($providers as $provider_name => $provider) {
|
| 93 |
$provider_options[$provider_name] = $provider['title'];
|
| 94 |
}
|
| 95 |
return $provider_options;
|
| 96 |
}
|
| 97 |
|
| 98 |
/**
|
| 99 |
* Retrieve a provider info array containing information about Views data,
|
| 100 |
* parse callback and more.
|
| 101 |
*
|
| 102 |
* @param $provider_name
|
| 103 |
* The name of the provider for which the provider info array should be
|
| 104 |
* retrieved.
|
| 105 |
* @param $value_type
|
| 106 |
* The given value type that is expected for this provider
|
| 107 |
* (e.g. 'nid' or 'hours').
|
| 108 |
*/
|
| 109 |
function value_provider($provider_name, $value_type) {
|
| 110 |
$providers = value_providers($value_type);
|
| 111 |
return isset($providers[$provider_name]) ? $providers[$provider_name] : NULL;
|
| 112 |
}
|
| 113 |
|
| 114 |
/**
|
| 115 |
* Add all fields of a given provider to a view, so that those can later
|
| 116 |
* be transformed to a property of each result item.
|
| 117 |
*/
|
| 118 |
function value_provider_add_fields(&$view, $display_id, $provider) {
|
| 119 |
$handler = &$view->display[$display_id]->handler;
|
| 120 |
|
| 121 |
foreach ($provider['columns'] as $column_property_name => $column_name) {
|
| 122 |
if (isset($provider['handler_name'][$column_name])) {
|
| 123 |
// Don't add additional columns, their handler is already
|
| 124 |
// being added by the main column (which is always specified).
|
| 125 |
continue;
|
| 126 |
}
|
| 127 |
$field_handler = views_get_handler(
|
| 128 |
$provider['views table'], $column_name, 'field'
|
| 129 |
);
|
| 130 |
|
| 131 |
if (array_key_exists($column_name, $view->get_items('field'))) {
|
| 132 |
$id = $column_name;
|
| 133 |
}
|
| 134 |
else if (is_object($field_handler) && !($field_handler instanceof views_handler_field_broken)) {
|
| 135 |
// Column is not yet in the view, we need to insert it.
|
| 136 |
// Only if the field handler actually exists, though.
|
| 137 |
$id = $view->add_item($display_id, 'field', $provider['views table'], $column_name);
|
| 138 |
}
|
| 139 |
|
| 140 |
$item = $view->get_item($display_id, 'field', $column_name);
|
| 141 |
if (!isset($item)) {
|
| 142 |
return FALSE;
|
| 143 |
}
|
| 144 |
$options = module_invoke_all('value_provider_field_handler_options', get_class($field_handler));
|
| 145 |
$item = array_merge($item, $options);
|
| 146 |
$view->set_item($display_id, 'field', $column_name, $item);
|
| 147 |
}
|
| 148 |
}
|
| 149 |
|
| 150 |
/**
|
| 151 |
* Transform the raw view results into an array of actual result objects
|
| 152 |
* or arrays by using the parse function of value providers.
|
| 153 |
*
|
| 154 |
* @param $view
|
| 155 |
* The view that has been executed and now contains its raw results
|
| 156 |
* in $view->result.
|
| 157 |
* @param $providers
|
| 158 |
* The value providers whose fields have been added to the view
|
| 159 |
* with value_provider_add_fields(). The array key of this list will
|
| 160 |
* determine the name of the corresponding result properties.
|
| 161 |
* @param $primary_key
|
| 162 |
* The view's primary key, e.g. 'nid' for node views or 'uid' for user views.
|
| 163 |
* @param $result_type
|
| 164 |
* If 'object' (default), this function will return an array of
|
| 165 |
* result objects. If 'array', it will return an array of arrays.
|
| 166 |
* Either way, the container array is keyed by the $primary_key,
|
| 167 |
* and also contains the $primary_key as property / array element.
|
| 168 |
*/
|
| 169 |
function value_provider_results($view, $providers, $primary_key, $result_type = 'object') {
|
| 170 |
$results = array();
|
| 171 |
foreach ($view->result as $row) {
|
| 172 |
$results[$row->$primary_key] = array(
|
| 173 |
$primary_key => $row->$primary_key,
|
| 174 |
);
|
| 175 |
if ($result_type == 'object') {
|
| 176 |
$results[$row->$primary_key] = (object) $results[$row->$primary_key];
|
| 177 |
}
|
| 178 |
$results[$row->$primary_key] = value_provider_merge_properties(
|
| 179 |
$view, $row, $results[$row->$primary_key], $providers
|
| 180 |
);
|
| 181 |
}
|
| 182 |
return $results;
|
| 183 |
}
|
| 184 |
|
| 185 |
/**
|
| 186 |
* Transform one raw view result into actual result properties by using
|
| 187 |
* the parse function of value providers, and merge these result properties
|
| 188 |
* into an existing object or array.
|
| 189 |
*
|
| 190 |
* @param $view
|
| 191 |
* The view that has been executed.
|
| 192 |
* @param $result
|
| 193 |
* A single result row of $view->result from the above view.
|
| 194 |
* @param $target
|
| 195 |
* The object or array that shall receive the parsed result properties.
|
| 196 |
* @param $providers
|
| 197 |
* The value providers whose fields have been added to the view
|
| 198 |
* with value_provider_add_fields(). The array key of this list will
|
| 199 |
* determine the name of the corresponding result properties.
|
| 200 |
* @return
|
| 201 |
* The $target object or array, extended by the additional result properties.
|
| 202 |
*/
|
| 203 |
function value_provider_merge_properties($view, $result, &$target, $providers) {
|
| 204 |
foreach ($providers as $value_name => $provider) {
|
| 205 |
$value_properties = array();
|
| 206 |
|
| 207 |
foreach ($provider['columns'] as $value_property_name => $column_name) {
|
| 208 |
$handler_name = isset($provider['handler_name'][$column_name])
|
| 209 |
? $provider['handler_name'][$column_name]
|
| 210 |
: $column_name;
|
| 211 |
|
| 212 |
$field_handler = $view->field[$handler_name];
|
| 213 |
$result_column = $field_handler->aliases[$column_name];
|
| 214 |
|
| 215 |
if (isset($result->$result_column)) {
|
| 216 |
$value_properties[$value_property_name] = $result->$result_column;
|
| 217 |
}
|
| 218 |
}
|
| 219 |
$parse_function = $provider['parse callback'];
|
| 220 |
|
| 221 |
if (is_object($target)) {
|
| 222 |
$target->$value_name = $parse_function($value_properties);
|
| 223 |
}
|
| 224 |
else {
|
| 225 |
$target[$value_name] = $parse_function($value_properties);
|
| 226 |
}
|
| 227 |
}
|
| 228 |
return $target;
|
| 229 |
}
|
| 230 |
|
| 231 |
/**
|
| 232 |
* Add a filter defined by a value provider to a view.
|
| 233 |
*
|
| 234 |
* @return
|
| 235 |
* TRUE if the filter was added successfully, or FALSE otherwise.
|
| 236 |
*/
|
| 237 |
function value_provider_add_filter(&$view, $display_id, $provider, $filter_id, $filter_options = NULL) {
|
| 238 |
$handler = &$view->display[$display_id]->handler;
|
| 239 |
|
| 240 |
$filter_name = $provider['filters'][$filter_id]['field'];
|
| 241 |
$filter_handler = views_get_handler($provider['views table'], $filter_name, 'filter');
|
| 242 |
|
| 243 |
if (!$filter_handler) {
|
| 244 |
return FALSE;
|
| 245 |
}
|
| 246 |
// Add the filter to the view.
|
| 247 |
$id = $view->add_item($display_id, 'filter', $provider['views table'], $filter_name);
|
| 248 |
$filters = $handler->get_option('filters');
|
| 249 |
|
| 250 |
$filter_function = $provider['filters'][$filter_id]['callback'];
|
| 251 |
$filter_function($filters[$id], $filter_handler, $filter_options);
|
| 252 |
|
| 253 |
$handler->set_option('filters', $filters);
|
| 254 |
return TRUE;
|
| 255 |
}
|
| 256 |
|
| 257 |
|
| 258 |
/**
|
| 259 |
* Implementation of hook_nodeapi(): Let value providers specify if they want
|
| 260 |
* to cause a hook_value_provider_[valuetype]_modified() call, which should
|
| 261 |
* happen when the value inside a node has been inserted, updated or deleted.
|
| 262 |
*/
|
| 263 |
function value_provider_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
|
| 264 |
switch ($op) {
|
| 265 |
case 'insert':
|
| 266 |
case 'update':
|
| 267 |
case 'delete':
|
| 268 |
$modified_providers = array();
|
| 269 |
|
| 270 |
foreach (value_providers() as $value_type => $providers) {
|
| 271 |
$notification_functions = array();
|
| 272 |
foreach ($providers as $provider_name => $provider) {
|
| 273 |
if (!empty($provider['nodeapi notifications'])) {
|
| 274 |
$notification_functions[$provider['nodeapi notifications']][$provider_name] = $provider;
|
| 275 |
}
|
| 276 |
}
|
| 277 |
if (!empty($notification_functions)) {
|
| 278 |
$modified_providers[$value_type] = array();
|
| 279 |
|
| 280 |
foreach ($notification_functions as $function => $function_providers) {
|
| 281 |
$function = $provider['nodeapi notifications'];
|
| 282 |
$modified_providers[$value_type] += $function($node, $op, $value_type, $providers);
|
| 283 |
}
|
| 284 |
}
|
| 285 |
}
|
| 286 |
|
| 287 |
if (!empty($modified_providers)) {
|
| 288 |
module_invoke_all('value_provider_modified', $node, $op, $modified_providers);
|
| 289 |
}
|
| 290 |
break;
|
| 291 |
}
|
| 292 |
}
|
| 293 |
|
| 294 |
/**
|
| 295 |
* Return a skeleton for CCK based value providers, with important stuff
|
| 296 |
* like tables, columns and title already filled in. The 'parse callback'
|
| 297 |
* and (optional) 'filters' properties are still missing from that array,
|
| 298 |
* those need to be added manually before adding the provider to the
|
| 299 |
* provider info array.
|
| 300 |
*/
|
| 301 |
function value_provider_content_provider_base($field) {
|
| 302 |
$field_db_info = content_database_info($field);
|
| 303 |
|
| 304 |
$provider = array(
|
| 305 |
'title' => value_provider_content_field_title($field),
|
| 306 |
'views table' => content_views_tablename($field),
|
| 307 |
'nodeapi notifications' => 'value_provider_content_nodeapi_notification',
|
| 308 |
'extra' => array('cck field' => $field),
|
| 309 |
);
|
| 310 |
|
| 311 |
foreach ($field_db_info['columns'] as $column_name => $column_info) {
|
| 312 |
$provider['columns'][$column_name] = $column_info['column'];
|
| 313 |
|
| 314 |
// CCK only provides a single handler that makes use of additional columns,
|
| 315 |
// and that one has the name of the first column. All other columns also
|
| 316 |
// need to specify that handler, and be added as additional columns to it.
|
| 317 |
if (!isset($first_column)) {
|
| 318 |
$first_column = $column_info['column'];
|
| 319 |
}
|
| 320 |
else {
|
| 321 |
$provider['handler_name'][$column_info['column']] = $first_column;
|
| 322 |
}
|
| 323 |
}
|
| 324 |
return $provider;
|
| 325 |
}
|
| 326 |
|
| 327 |
/**
|
| 328 |
* Return a list of providers whose values have been modified on a
|
| 329 |
* hook_nodeapi() insert, update or delete operation.
|
| 330 |
*/
|
| 331 |
function value_provider_content_nodeapi_notification($node, $op, $value_type, $providers) {
|
| 332 |
$modified_providers = array();
|
| 333 |
|
| 334 |
foreach ($providers as $provider_name => $provider) {
|
| 335 |
$field_name = $provider['extra']['cck field']['field_name'];
|
| 336 |
$field = content_fields($field_name, $node->type);
|
| 337 |
|
| 338 |
if (!empty($field)) {
|
| 339 |
$modified_providers[$provider_name] = $provider;
|
| 340 |
}
|
| 341 |
}
|
| 342 |
return $modified_providers;
|
| 343 |
}
|
| 344 |
|
| 345 |
/**
|
| 346 |
* Return a user visible string representation for a given CCK field.
|
| 347 |
*/
|
| 348 |
function value_provider_content_field_title($field) {
|
| 349 |
$label = $field['widget']['label'];
|
| 350 |
|
| 351 |
if (empty($label)) {
|
| 352 |
return $field['field_name'];
|
| 353 |
}
|
| 354 |
else {
|
| 355 |
return t('@label (@fieldname)', array(
|
| 356 |
'@label' => $label, '@fieldname' => $field['field_name']
|
| 357 |
));
|
| 358 |
}
|
| 359 |
}
|
| 360 |
|
| 361 |
/**
|
| 362 |
* Implementation of hook_value_provider_field_handler_options():
|
| 363 |
* Provide default options for field handlers.
|
| 364 |
*/
|
| 365 |
function value_provider_value_provider_field_handler_options($handler_class) {
|
| 366 |
switch ($handler_class) {
|
| 367 |
case 'content_handler_field_multiple':
|
| 368 |
return array(
|
| 369 |
'multiple' => array(
|
| 370 |
'group' => FALSE,
|
| 371 |
),
|
| 372 |
);
|
| 373 |
}
|
| 374 |
}
|