base.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <?php
  2. /**
  3. * @file
  4. * Definition of views_object.
  5. */
  6. /**
  7. * Provides the basic object definitions used by plugins and handlers.
  8. */
  9. class views_object {
  10. /**
  11. * Except for displays, options for the object will be held here.
  12. */
  13. public $options = array();
  14. /**
  15. * The top object of a view.
  16. *
  17. * @var view
  18. */
  19. public $view = NULL;
  20. /**
  21. * Handler's definition.
  22. *
  23. * @var array
  24. */
  25. public $definition;
  26. /**
  27. * Information about options for all kinds of purposes will be held here.
  28. *
  29. * @code
  30. * 'option_name' => array(
  31. * - 'default' => default value,
  32. * - 'translatable' => (optional) TRUE/FALSE (wrap in t() on export if true),
  33. * - 'contains' => (optional) array of items this contains, with its own
  34. * defaults, etc. If contains is set, the default will be ignored and
  35. * assumed to be array().
  36. * - 'bool' => (optional) TRUE/FALSE Is the value a boolean value. This will
  37. * change the export format to TRUE/FALSE instead of 1/0.
  38. * - 'export' => (optional) FALSE or a callback for special export handling
  39. * if necessary.
  40. * - 'unpack_translatable' => (optional) callback for special handling for
  41. * translating data within the option, if necessary.
  42. * ),
  43. *
  44. * @return array
  45. * Returns the options of this handler/plugin.
  46. *
  47. * @see views_object::export_option()
  48. * @see views_object::export_option_always()
  49. * @see views_object::unpack_translatable()
  50. */
  51. public function option_definition() {
  52. return array();
  53. }
  54. /**
  55. * Collect this handler's option definition and alter them, ready for use.
  56. *
  57. * @return array
  58. * Returns the options of this handler/plugin after allowing for alters.
  59. *
  60. * @see hook_views_plugin_option_definition_alter()
  61. * @see hook_views_handler_option_definition_alter()
  62. */
  63. function altered_option_definition() {
  64. $definition = $this->option_definition();
  65. if (!empty($this->is_plugin)) {
  66. // Trigger hook_views_plugin_option_definition_alter().
  67. drupal_alter('views_plugin_option_definition', $definition, $this);
  68. }
  69. else {
  70. // Trigger hook_views_handler_option_definition_alter().
  71. drupal_alter('views_handler_option_definition', $definition, $this);
  72. }
  73. return $definition;
  74. }
  75. /**
  76. * Views handlers use a special construct function.
  77. *
  78. * Allows it to more easily construct them with variable arguments.
  79. */
  80. public function construct() {
  81. $this->set_default_options();
  82. }
  83. /**
  84. * Set default options on this object.
  85. *
  86. * Called by the constructor in a complex chain to deal with backward
  87. * compatibility.
  88. *
  89. * @deprecated since views2
  90. */
  91. public function options(&$options) {
  92. }
  93. /**
  94. * Set default options.
  95. *
  96. * For backward compatibility, it sends the options array; this is a feature
  97. * that will likely disappear at some point.
  98. */
  99. public function set_default_options() {
  100. $this->_set_option_defaults($this->options, $this->altered_option_definition());
  101. // Retained for complex defaults plus backward compatibility.
  102. $this->options($this->options);
  103. }
  104. /**
  105. *
  106. */
  107. public function _set_option_defaults(&$storage, $options, $level = 0) {
  108. foreach ($options as $option => $definition) {
  109. if (isset($definition['contains']) && is_array($definition['contains'])) {
  110. $storage[$option] = array();
  111. $this->_set_option_defaults($storage[$option], $definition['contains'], $level++);
  112. }
  113. elseif (!empty($definition['translatable']) && !empty($definition['default'])) {
  114. $storage[$option] = t($definition['default']);
  115. }
  116. else {
  117. $storage[$option] = isset($definition['default']) ? $definition['default'] : NULL;
  118. }
  119. }
  120. }
  121. /**
  122. * Unpack options over our existing defaults, drilling down into arrays so
  123. * that defaults don't get totally blown away.
  124. */
  125. public function unpack_options(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE, $localization_keys = array()) {
  126. if ($check && !is_array($options)) {
  127. return;
  128. }
  129. if (!isset($definition)) {
  130. $definition = $this->altered_option_definition();
  131. }
  132. if (!empty($this->view)) {
  133. // Ensure we have a localization plugin.
  134. $this->view->init_localization();
  135. // Set up default localization keys. Handlers and such set this for us.
  136. if (empty($localization_keys) && isset($this->localization_keys)) {
  137. $localization_keys = $this->localization_keys;
  138. }
  139. // but plugins don't because there isn't a common init() these days.
  140. elseif (!empty($this->is_plugin) && empty($localization_keys)) {
  141. if ($this->plugin_type != 'display') {
  142. $localization_keys = array($this->view->current_display);
  143. $localization_keys[] = $this->plugin_type;
  144. }
  145. }
  146. }
  147. foreach ($options as $key => $value) {
  148. if (is_array($value)) {
  149. // Ignore arrays with no definition.
  150. if (!$all && empty($definition[$key])) {
  151. continue;
  152. }
  153. if (!isset($storage[$key]) || !is_array($storage[$key])) {
  154. $storage[$key] = array();
  155. }
  156. // If we're just unpacking our known options, and we're dropping an
  157. // unknown array (as might happen for a dependent plugin fields) go
  158. // ahead and drop that in.
  159. if (!$all && isset($definition[$key]) && !isset($definition[$key]['contains'])) {
  160. $storage[$key] = $value;
  161. continue;
  162. }
  163. $this->unpack_options($storage[$key], $value, isset($definition[$key]['contains']) ? $definition[$key]['contains'] : array(), $all, FALSE, array_merge($localization_keys, array($key)));
  164. }
  165. // Don't localize strings during editing. When editing, we need to work
  166. // with the original data, not the translated version.
  167. elseif (empty($this->view->editing) && !empty($definition[$key]['translatable']) && !empty($value) || !empty($definition['contains'][$key]['translatable']) && !empty($value)) {
  168. if (!empty($this->view) && $this->view->is_translatable()) {
  169. // Allow other modules to make changes to the string before it's
  170. // sent for translation.
  171. // The $keys array is built from the view name, any localization keys
  172. // sent in, and the name of the property being processed.
  173. $format = NULL;
  174. if (isset($definition[$key]['format_key']) && isset($options[$definition[$key]['format_key']])) {
  175. $format = $options[$definition[$key]['format_key']];
  176. }
  177. $translation_data = array(
  178. 'value' => $value,
  179. 'format' => $format,
  180. 'keys' => array_merge(array($this->view->name), $localization_keys, array($key)),
  181. );
  182. $storage[$key] = $this->view->localization_plugin->translate($translation_data);
  183. }
  184. // Otherwise, this is a code-based string, so we can use t().
  185. else {
  186. $storage[$key] = t($value);
  187. }
  188. }
  189. elseif ($all || !empty($definition[$key])) {
  190. $storage[$key] = $value;
  191. }
  192. }
  193. }
  194. /**
  195. * Let the handler know what its full definition is.
  196. */
  197. public function set_definition($definition) {
  198. $this->definition = $definition;
  199. if (isset($definition['field'])) {
  200. $this->real_field = $definition['field'];
  201. }
  202. }
  203. /**
  204. * Destructor.
  205. */
  206. public function destroy() {
  207. if (isset($this->view)) {
  208. unset($this->view);
  209. }
  210. if (isset($this->display)) {
  211. unset($this->display);
  212. }
  213. if (isset($this->query)) {
  214. unset($this->query);
  215. }
  216. }
  217. /**
  218. *
  219. */
  220. public function export_options($indent, $prefix) {
  221. $output = '';
  222. foreach ($this->altered_option_definition() as $option => $definition) {
  223. $output .= $this->export_option($indent, $prefix, $this->options, $option, $definition, array());
  224. }
  225. return $output;
  226. }
  227. /**
  228. *
  229. */
  230. public function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
  231. // Do not export options for which we have no settings.
  232. if (!isset($storage[$option])) {
  233. return;
  234. }
  235. if (isset($definition['export'])) {
  236. if ($definition['export'] === FALSE) {
  237. return;
  238. }
  239. // Special handling for some items.
  240. if (method_exists($this, $definition['export'])) {
  241. return $this->{$definition['export']}($indent, $prefix, $storage, $option, $definition, $parents);
  242. }
  243. }
  244. // Add the current option to the parents tree.
  245. $parents[] = $option;
  246. $output = '';
  247. // If it has child items, export those separately.
  248. if (isset($definition['contains'])) {
  249. foreach ($definition['contains'] as $sub_option => $sub_definition) {
  250. $output .= $this->export_option($indent, $prefix, $storage[$option], $sub_option, $sub_definition, $parents);
  251. }
  252. }
  253. // Otherwise export just this item.
  254. else {
  255. $default = isset($definition['default']) ? $definition['default'] : NULL;
  256. $value = $storage[$option];
  257. if (isset($definition['bool'])) {
  258. $value = (bool) $value;
  259. }
  260. if ($value !== $default) {
  261. $output .= $indent . $prefix . "['" . implode("']['", $parents) . "'] = ";
  262. if (isset($definition['bool'])) {
  263. $output .= empty($storage[$option]) ? 'FALSE' : 'TRUE';
  264. }
  265. else {
  266. $output .= views_var_export($storage[$option], $indent);
  267. }
  268. $output .= ";\n";
  269. }
  270. }
  271. return $output;
  272. }
  273. /**
  274. * Always exports the option, regardless of the default value.
  275. */
  276. public function export_option_always($indent, $prefix, $storage, $option, $definition, $parents) {
  277. // If there is no default, the option will always be exported.
  278. unset($definition['default']);
  279. // Unset our export method to prevent recursion.
  280. unset($definition['export']);
  281. return $this->export_option($indent, $prefix, $storage, $option, $definition, $parents);
  282. }
  283. /**
  284. * Unpacks each handler to store translatable texts.
  285. */
  286. public function unpack_translatables(&$translatable, $parents = array()) {
  287. foreach ($this->altered_option_definition() as $option => $definition) {
  288. $this->unpack_translatable($translatable, $this->options, $option, $definition, $parents, array());
  289. }
  290. }
  291. /**
  292. * Unpack a single option definition.
  293. *
  294. * This function run's through all suboptions recursive.
  295. *
  296. * @param array $translatable
  297. * Stores all available translatable items.
  298. * @param array $storage
  299. * @param string $option
  300. * @param string $definition
  301. * @param array $parents
  302. * @param array $keys
  303. */
  304. public function unpack_translatable(&$translatable, $storage, $option, $definition, $parents, $keys = array()) {
  305. // Do not export options for which we have no settings.
  306. if (!isset($storage[$option])) {
  307. return;
  308. }
  309. // Special handling for some items.
  310. if (isset($definition['unpack_translatable']) && method_exists($this, $definition['unpack_translatable'])) {
  311. return $this->{$definition['unpack_translatable']}($translatable, $storage, $option, $definition, $parents, $keys);
  312. }
  313. if (isset($definition['translatable'])) {
  314. if ($definition['translatable'] === FALSE) {
  315. return;
  316. }
  317. }
  318. // Add the current option to the parents tree.
  319. $parents[] = $option;
  320. // If it has child items, unpack those separately.
  321. if (isset($definition['contains'])) {
  322. foreach ($definition['contains'] as $sub_option => $sub_definition) {
  323. $translation_keys = array_merge($keys, array($sub_option));
  324. $this->unpack_translatable($translatable, $storage[$option], $sub_option, $sub_definition, $parents, $translation_keys);
  325. }
  326. }
  327. // @todo Figure out this double definition stuff.
  328. $options = $storage[$option];
  329. if (is_array($options)) {
  330. foreach ($options as $key => $value) {
  331. $translation_keys = array_merge($keys, array($key));
  332. if (is_array($value)) {
  333. $this->unpack_translatable($translatable, $options, $key, $definition, $parents, $translation_keys);
  334. }
  335. elseif (!empty($definition[$key]['translatable']) && !empty($value)) {
  336. // Build source data and add to the array.
  337. $format = NULL;
  338. if (isset($definition['format_key']) && isset($options[$definition['format_key']])) {
  339. $format = $options[$definition['format_key']];
  340. }
  341. $translatable[] = array(
  342. 'value' => $value,
  343. 'keys' => $translation_keys,
  344. 'format' => $format,
  345. );
  346. }
  347. }
  348. }
  349. elseif (!empty($definition['translatable']) && !empty($options)) {
  350. $value = $options;
  351. // Build source data and add to the array.
  352. $format = NULL;
  353. if (isset($definition['format_key']) && isset($storage[$definition['format_key']])) {
  354. $format = $storage[$definition['format_key']];
  355. }
  356. $translatable[] = array(
  357. 'value' => $value,
  358. 'keys' => isset($translation_keys) ? $translation_keys : $parents,
  359. 'format' => $format,
  360. );
  361. }
  362. }
  363. }