Ticket #6357: 6357---.diff

File 6357---.diff, 19.7 KB (added by andy, 5 years ago)
Line 
1Index: wp-includes/taxonomy.php
2===================================================================
3--- wp-includes/taxonomy.php    (revision 7519)
4+++ wp-includes/taxonomy.php    (working copy)
5@@ -34,15 +34,23 @@
6  *
7  * @uses $wp_taxonomies
8  *
9- * @param array|string $object_type Name of the type of taxonomy object
10+ * @param array|string|object $object Name of the type of taxonomy object, or an object (row from posts)
11  * @return array The names of all taxonomy of $object_type.
12  */
13-function get_object_taxonomies($object_type) {
14+function get_object_taxonomies($object) {
15        global $wp_taxonomies;
16 
17+       if ( is_object($object) ) {
18+               if ( $object->post_type == 'attachment' )
19+                       return get_attachment_taxonomies($object);
20+               $object = $object->post_type;
21+       }
22+
23+       $object = (array) $object;
24+
25        $taxonomies = array();
26        foreach ( $wp_taxonomies as $taxonomy ) {
27-               if ( in_array($object_type, (array) $taxonomy->object_type) )
28+               if ( array_intersect($object, (array) $taxonomy->object_type) )
29                        $taxonomies[] = $taxonomy->name;
30        }
31 
32@@ -119,44 +127,51 @@
33 }
34 
35 /**
36- * register_taxonomy() - Create or modify a taxonomy object.
37+ * register_taxonomy() - Create or modify a taxonomy object. Do not use before init.
38  *
39  * A simple function for creating or modifying a taxonomy object based on the parameters given.
40  * The function will accept an array (third optional parameter), along with strings for the
41  * taxonomy name and another string for the object type.
42  *
43- * The function keeps a default set, allowing for the $args to be optional but allow the other
44- * functions to still work. It is possible to overwrite the default set, which contains two
45- * keys: hierarchical and update_count_callback.
46- *
47  * Nothing is returned, so expect error maybe or use is_taxonomy() to check whether taxonomy exists.
48  *
49  * Optional $args contents:
50  * hierarachical - has some defined purpose at other parts of the API and is a boolean value.
51  * update_count_callback - works much like a hook, in that it will be called when the count is updated.
52+ * rewrite - false to prevent rewrite, or array('slug'=>$slug) to customize permastruct; default will use $taxonomy as slug
53+ * query_var - false to prevent queries, or string to customize query var (?$query_var=$term); default will use $taxonomy as query var
54  *
55  * @package WordPress
56  * @subpackage Taxonomy
57  * @since 2.3
58  * @uses $wp_taxonomies Inserts new taxonomy object into the list
59+ * @uses $wp_rewrite Adds rewrite tags and permastructs
60+ * @uses $wp Adds query vars
61  *
62  * @param string $taxonomy Name of taxonomy object
63  * @param array|string $object_type Name of the object type for the taxonomy object.
64  * @param array|string $args See above description for the two keys values.
65  */
66 function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
67-       global $wp_taxonomies, $wp_rewrite;
68+       global $wp_taxonomies, $wp_rewrite, $wp;
69 
70-       $defaults = array('hierarchical' => false, 'update_count_callback' => '');
71+       $defaults = array('hierarchical' => false, 'update_count_callback' => '', 'rewrite' => true, 'query_var' => true);
72        $args = wp_parse_args($args, $defaults);
73 
74-       if ( !empty( $args['rewrite'] ) ) {
75+       if ( false !== $args['query_var'] ) {
76+               if ( empty($args['query_var']) )
77+                       $args['query_var'] = $taxonomy;
78+               $args['query_var'] = sanitize_title_with_dashes($args['query_var']);
79+               $wp->add_query_var($args['query_var']);
80+       }
81+
82+       if ( false !== $args['rewrite'] ) {
83                if ( !is_array($args['rewrite']) )
84                        $args['rewrite'] = array();
85                if ( !isset($args['rewrite']['slug']) )
86                        $args['rewrite']['slug'] = sanitize_title_with_dashes($taxonomy);
87-               $wp_rewrite->add_rewrite_tag("%$taxonomy%", '([^/]+)', "taxonomy=$taxonomy&term=");
88-               $wp_rewrite->add_permastruct("{$args['rewrite']['slug']}/%$taxonomy%");
89+               $wp_rewrite->add_rewrite_tag("%$taxonomy%", '([^/]+)', $args['query_var'] ? "{$args['query_var']}=" : "taxonomy=$taxonomy&term=$term");
90+               $wp_rewrite->add_permastruct($taxonomy, "{$args['rewrite']['slug']}/%$taxonomy%");
91        }
92 
93        $args['name'] = $taxonomy;
94@@ -1036,6 +1051,22 @@
95 
96        $defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all');
97        $args = wp_parse_args( $args, $defaults );
98+
99+       $terms = array();
100+       if ( count($taxonomies) > 1 ) {
101+               foreach ( $taxonomies as $index => $taxonomy ) {
102+                       $t = get_taxonomy($taxonomy);
103+                       if ( is_array($t->args) && $args != array_merge($args, $t->args) ) {
104+                               unset($taxonomies[$index]);
105+                               $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
106+                       }
107+               }
108+       } else {
109+               $t = get_taxonomy($taxonomies[0]);
110+               if ( is_array($t->args) )
111+                       $args = array_merge($args, $t->args);
112+       }
113+
114        extract($args, EXTR_SKIP);
115 
116        if ( 'count' == $orderby )
117@@ -1067,10 +1098,10 @@
118        $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids) ORDER BY $orderby $order";
119 
120        if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
121-               $terms = $wpdb->get_results($query);
122+               $terms = array_merge($terms, $wpdb->get_results($query));
123                update_term_cache($terms);
124        } else if ( 'ids' == $fields || 'names' == $fields ) {
125-               $terms = $wpdb->get_col($query);
126+               $terms = array_merge($terms, $wpdb->get_col($query));
127        } else if ( 'tt_ids' == $fields ) {
128                $terms = $wpdb->get_col("SELECT tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) ORDER BY tr.term_taxonomy_id $order");
129        }
130@@ -1899,4 +1930,99 @@
131        }
132 }
133 
134+/**
135+ * get_term_link() - Generates a permalink for a taxonomy term archive
136+ *
137+ * @param object|int|string $term
138+ * @param string $taxonomy
139+ * @return string HTML link to taxonomy term archive
140+ */
141+function get_term_link( $term, $taxonomy ) {
142+       global $wp_rewrite;
143+
144+       $termlink = $wp_rewrite->get_extra_permastruct($taxonomy);
145+
146+       if ( !is_object($term) ) {
147+               if ( is_int($term) ) {
148+                       $term = &get_term($term, $taxonomy);
149+               } else {
150+                       $term = &get_term_by('slug', $term, $taxonomy);
151+               }
152+       }
153+       if ( is_wp_error( $term ) )
154+               return $term;
155+
156+       $slug = $term->slug;
157+
158+       if ( empty($termlink) ) {
159+               $file = get_option('home') . '/';
160+               $t = get_taxonomy($taxonomy);
161+               if ( $t->query_var )
162+                       $termlink = "$file?$t->query_var=$slug";
163+               else
164+                       $termlink = "$file?taxonomy=$taxonomy&term=$slug";
165+       } else {
166+               $termlink = str_replace("%$taxonomy%", $slug, $termlink);
167+               $termlink = get_option('home') . user_trailingslashit($termlink, 'category');
168+       }
169+       return apply_filters('term_link', $termlink, $term, $taxonomy);
170+}
171+
172+function the_taxonomies($args = array()) {
173+       $defaults = array(
174+               'post' => 0,
175+               'before' => '',
176+               'sep' => ' ',
177+               'after' => '',
178+       );
179+
180+       $r = wp_parse_args( $args, $defaults );
181+       extract( $r, EXTR_SKIP );
182+
183+       echo $before . join($sep, get_the_taxonomies($post)) . $after;
184+}
185+
186+function get_the_taxonomies($post = 0) {
187+       if ( is_int($post) )
188+               $post =& get_post($post);
189+       elseif ( !is_object($post) )
190+               $post =& $GLOBALS['post'];
191+
192+       $taxonomies = array();
193+
194+       if ( !$post )
195+               return $taxonomies;
196+
197+       $_template = '%s: %l.';
198+
199+       foreach ( get_object_taxonomies($post) as $taxonomy ) {
200+               $t = (array) get_taxonomy($taxonomy);
201+               if ( empty($t['label']) )
202+                       $t['label'] = $taxonomy;
203+               if ( empty($t['args']) )
204+                       $t['args'] = array();
205+               if ( empty($t['template']) )
206+                       $t['template'] = $_template;
207+
208+               $terms = get_object_term_cache($post->ID, $taxonomy);
209+               if ( empty($terms) )
210+                       $terms = wp_get_object_terms($post->ID, $taxonomy, $t['args']);
211+
212+               $links = array();
213+
214+               foreach ( $terms as $term )
215+                       $links[] = "<a href='" . attribute_escape(get_term_link($term, $taxonomy)) . "'>$term->name</a>";
216+
217+               if ( $links )
218+                       $taxonomies[$taxonomy] = wp_sprintf($t['template'], $t['label'], $links, $terms);
219+       }
220+       return $taxonomies;
221+}
222+
223+function get_post_taxonomies($post = 0) {
224+       $post =& get_post($post);
225+
226+       return get_object_taxonomies($post);
227+}
228+
229 ?>
230Index: wp-includes/media.php
231===================================================================
232--- wp-includes/media.php       (revision 7519)
233+++ wp-includes/media.php       (working copy)
234@@ -438,4 +438,35 @@
235                echo wp_get_attachment_link($attachments[$k]->ID, 'thumbnail', true);
236 }
237 
238+function get_attachment_taxonomies($attachment) {
239+       if ( is_int( $attachment ) )
240+               $attachment = get_post($attachment);
241+       else if ( is_array($attachment) )
242+               $attachment = (object) $attachment;
243+
244+       if ( ! is_object($attachment) )
245+               return array();
246+
247+       $filename = basename($attachment->guid);
248+
249+       $objects = array('attachment');
250+
251+       if ( false !== strpos($filename, '.') )
252+               $objects[] = 'attachment:' . substr($filename, strrpos($filename, '.') + 1);
253+       if ( !empty($attachment->post_mime_type) ) {
254+               $objects[] = 'attachment:' . $attachment->post_mime_type;
255+               if ( false !== strpos($attachment->post_mime_type, '/') )
256+                       foreach ( explode('/', $attachment->post_mime_type) as $token )
257+                               if ( !empty($token) )
258+                                       $objects[] = "attachment:$token";
259+       }
260+
261+       $taxonomies = array();
262+       foreach ( $objects as $object )
263+               if ( $taxes = get_object_taxonomies($object) )
264+                       $taxonomies = array_merge($taxonomies, $taxes);
265+
266+       return array_unique($taxonomies);
267+}
268+
269 ?>
270Index: wp-includes/query.php
271===================================================================
272--- wp-includes/query.php       (revision 7519)
273+++ wp-includes/query.php       (working copy)
274@@ -680,6 +680,12 @@
275 
276                        if ( empty($qv['taxonomy']) || empty($qv['term']) ) {
277                                $this->is_tax = false;
278+                               foreach ( $GLOBALS['wp_taxonomies'] as $t ) {
279+                                       if ( isset($t->query_var) && '' != $qv[$t->query_var] ) {
280+                                               $this->is_tax = true;
281+                                               break;
282+                                       }
283+                               }
284                        } else {
285                                $this->is_tax = true;
286                        }
287@@ -1146,19 +1152,34 @@
288 
289                // Taxonomies
290                if ( $this->is_tax ) {
291-                       $terms = get_terms($q['taxonomy'], array('slug'=>$q['term']));
292-                       foreach ( $terms as $term )
293-                               $term_ids[] = $term->term_id;
294-                       $post_ids = get_objects_in_term($term_ids, $q['taxonomy']);
295-
296-                       if ( count($post_ids) ) {
297-                               $whichcat .= " AND $wpdb->posts.ID IN (" . implode(', ', $post_ids) . ") ";
298-                               $post_type = 'any';
299-                               $q['post_status'] = 'publish';
300-                               $post_status_join = true;
301+                       if ( '' != $q['taxonomy'] ) {
302+                               $taxonomy = $q['taxonomy'];
303+                               $tt[$taxonomy] = $q['term'];
304+                               $terms = get_terms($q['taxonomy'], array('slug'=>$q['term']));
305                        } else {
306-                               $whichcat = " AND 0 = 1";
307+                               foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
308+                                       if ( isset($t->query_var) && '' != $q[$t->query_var] ) {
309+                                               $terms = get_terms($taxonomy, array('slug'=>$q[$t->query_var]));
310+                                               if ( !is_wp_error($terms) )
311+                                                       break;
312+                                       }
313+                               }
314                        }
315+                       if ( is_wp_error($terms) || empty($terms) ) {
316+                               $whichcat = " AND 0 ";
317+                       } else {
318+                               foreach ( $terms as $term )
319+                                       $term_ids[] = $term->term_id;
320+                               $post_ids = get_objects_in_term($term_ids, $taxonomy);
321+                               if ( !is_wp_error($post_ids) && count($post_ids) ) {
322+                                       $whichcat .= " AND $wpdb->posts.ID IN (" . implode(', ', $post_ids) . ") ";
323+                                       $post_type = 'any';
324+                                       $q['post_status'] = 'publish';
325+                                       $post_status_join = true;
326+                               } else {
327+                                       $whichcat = " AND 0 ";
328+                               }
329+                       }
330                }
331 
332                // Author/user stuff
333@@ -1296,7 +1317,7 @@
334                                        $statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
335                        }
336                        if ( $post_status_join ) {
337-                               $join .= " INNER JOIN $wpdb->posts AS p2 ON ($wpdb->posts.post_parent = p2.ID) ";
338+                               $join .= " LEFT JOIN $wpdb->posts AS p2 ON ($wpdb->posts.post_parent = p2.ID) ";
339                                foreach ( $statuswheres as $index => $statuswhere )
340                                        $statuswheres[$index] = "($statuswhere OR ($wpdb->posts.post_status = 'inherit' AND " . str_replace($wpdb->posts, 'p2', $statuswhere) . "))";
341                        }
342Index: wp-includes/formatting.php
343===================================================================
344--- wp-includes/formatting.php  (revision 7519)
345+++ wp-includes/formatting.php  (working copy)
346@@ -1376,7 +1376,7 @@
347        $args = (array) $args;
348        $result = array_shift($args);
349        if ( count($args) == 1 )
350-               $result .= $l['between_two'] . array_shift($args);
351+               $result .= $l['between_only_two'] . array_shift($args);
352        // Loop when more than two args
353        while ( count($args) ) {
354                $arg = array_shift($args);
355Index: wp-includes/rewrite.php
356===================================================================
357--- wp-includes/rewrite.php     (revision 7519)
358+++ wp-includes/rewrite.php     (working copy)
359@@ -461,6 +461,12 @@
360                return $this->tag_structure;
361        }
362 
363+       function get_extra_permastruct($name) {
364+               if ( isset($this->extra_permastructs[$name]) )
365+                       return $this->extra_permastructs[$name];
366+               return false;
367+       }
368+
369        function get_author_permastruct() {
370                if (isset($this->author_structure)) {
371                        return $this->author_structure;
372@@ -832,15 +838,14 @@
373                $page_rewrite = apply_filters('page_rewrite_rules', $page_rewrite);
374 
375                // Extra permastructs
376-               $extra_rewrite = array();
377                foreach ( $this->extra_permastructs as $permastruct )
378-                       $extra_rewrite = array_merge($extra_rewrite, $this->generate_rewrite_rules($permastruct, EP_NONE));
379+                       $this->extra_rules_top = array_merge($this->extra_rules_top, $this->generate_rewrite_rules($permastruct, EP_NONE));
380 
381                // Put them together.
382                if ( $this->use_verbose_page_rules )
383-                       $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $page_rewrite, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $extra_rewrite, $this->extra_rules);
384+                       $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $page_rewrite, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $this->extra_rules);
385                else
386-                       $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $extra_rewrite, $page_rewrite, $this->extra_rules);
387+                       $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules);
388 
389                do_action_ref_array('generate_rewrite_rules', array(&$this));
390                $this->rules = apply_filters('rewrite_rules_array', $this->rules);
391@@ -954,10 +959,10 @@
392                $wp->add_query_var($name);
393        }
394 
395-       function add_permastruct($struct, $with_front = true) {
396+       function add_permastruct($name, $struct, $with_front = true) {
397                if ( $with_front )
398                        $struct = $this->front . $struct;
399-               $this->extra_permastructs[] = $struct;
400+               $this->extra_permastructs[$name] = $struct;
401        }
402 
403        function flush_rules() {
404Index: wp-includes/classes.php
405===================================================================
406--- wp-includes/classes.php     (revision 7519)
407+++ wp-includes/classes.php     (working copy)
408@@ -14,7 +14,8 @@
409        var $did_permalink = false;
410 
411        function add_query_var($qv) {
412-               $this->public_query_vars[] = $qv;
413+               if ( !in_array($qv, $this->public_query_vars) )
414+                       $this->public_query_vars[] = $qv;
415        }
416 
417        function set_query_var($key, $value) {
418Index: wp-includes/category-template.php
419===================================================================
420--- wp-includes/category-template.php   (revision 7519)
421+++ wp-includes/category-template.php   (working copy)
422@@ -475,6 +475,18 @@
423 }
424 
425 function get_the_tags( $id = 0 ) {
426+       return apply_filters( 'get_the_tags', get_the_terms($id, 'post_tag') );
427+}
428+
429+function get_the_tag_list( $before = '', $sep = '', $after = '' ) {
430+       return apply_filters( 'the_tags', get_the_term_list(0, 'post_tag', $before, $sep, $after) );
431+}
432+
433+function the_tags( $before = 'Tags: ', $sep = ', ', $after = '' ) {
434+       return the_terms( 0, 'post_tag', $before, $sep, $after );
435+}
436+
437+function get_the_terms( $id = 0, $taxonomy ) {
438        global $post;
439 
440        $id = (int) $id;
441@@ -485,41 +497,39 @@
442        if ( !$id )
443                $id = (int) $post->ID;
444 
445-       $tags = get_object_term_cache($id, 'post_tag');
446-       if ( false === $tags )
447-               $tags = wp_get_object_terms($id, 'post_tag');
448+       $terms = get_object_term_cache($id, $taxonomy);
449+       if ( false === $terms )
450+               $terms = wp_get_object_terms($id, $taxonomy);
451 
452-       $tags = apply_filters( 'get_the_tags', $tags );
453-       if ( empty( $tags ) )
454+       if ( empty( $terms ) )
455                return false;
456-       return $tags;
457+
458+       return $terms;
459 }
460 
461-function get_the_tag_list( $before = '', $sep = '', $after = '' ) {
462-       $tags = get_the_tags();
463+function get_the_term_list( $id = 0, $taxonomy, $before = '', $sep = '', $after = '' ) {
464+       $terms = get_the_terms($id, $taxonomy);
465 
466-       if ( empty( $tags ) )
467+       if ( is_wp_error($terms) )
468+               return $terms;
469+
470+       if ( empty( $terms ) )
471                return false;
472 
473-       $tag_list = $before;
474-       foreach ( $tags as $tag ) {
475-               $link = get_tag_link($tag->term_id);
476+       foreach ( $terms as $term ) {
477+               $link = get_term_link($term, $taxonomy);
478                if ( is_wp_error( $link ) )
479                        return $link;
480-               $tag_links[] = '<a href="' . $link . '" rel="tag">' . $tag->name . '</a>';
481+               $term_links[] = '<a href="' . $link . '" rel="tag">' . $term->name . '</a>';
482        }
483 
484-       $tag_links = join( $sep, $tag_links );
485-       $tag_links = apply_filters( 'the_tags', $tag_links );
486-       $tag_list .= $tag_links;
487+       $term_links = apply_filters( "term_links-$taxonomy", $term_links );
488 
489-       $tag_list .= $after;
490-
491-       return $tag_list;
492+       return $before . join($sep, $term_links) . $after;
493 }
494 
495-function the_tags( $before = 'Tags: ', $sep = ', ', $after = '' ) {
496-       $return = get_the_tag_list($before, $sep, $after);
497+function the_terms( $id, $taxonomy, $before = '', $sep = '', $after = '' ) {
498+       $return = get_the_term_list($id, $taxonomy, $before, $sep, $after);
499        if ( is_wp_error( $return ) )
500                return false;
501        else
502Index: wp-content/themes/default/image.php
503===================================================================
504--- wp-content/themes/default/image.php (revision 7519)
505+++ wp-content/themes/default/image.php (working copy)
506@@ -23,6 +23,7 @@
507                                        <small>
508                                                This entry was posted on <?php the_time('l, F jS, Y') ?> at <?php the_time() ?>
509                                                and is filed under <?php the_category(', ') ?>.
510+                                               <?php the_taxonomies(); ?>
511                                                You can follow any responses to this entry through the <?php post_comments_feed_link('RSS 2.0'); ?> feed.
512 
513                                                <?php if (('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
514Index: wp-admin/includes/media.php
515===================================================================
516--- wp-admin/includes/media.php (revision 7519)
517+++ wp-admin/includes/media.php (working copy)
518@@ -426,37 +426,6 @@
519        return wp_iframe( 'media_upload_library_form', $errors );
520 }
521 
522-function get_attachment_taxonomies($attachment) {
523-       if ( is_int( $attachment ) )
524-               $attachment = get_post($attachment);
525-       else if ( is_array($attachment) )
526-               $attachment = (object) $attachment;
527-
528-       if ( ! is_object($attachment) )
529-               return array();
530-
531-       $filename = basename($attachment->guid);
532-
533-       $objects = array('attachment');
534-
535-       if ( false !== strpos($filename, '.') )
536-               $objects[] = 'attachment:' . substr($filename, strrpos($filename, '.') + 1);
537-       if ( !empty($attachment->post_mime_type) ) {
538-               $objects[] = 'attachment:' . $attachment->post_mime_type;
539-               if ( false !== strpos($attachment->post_mime_type, '/') )
540-                       foreach ( explode('/', $attachment->post_mime_type) as $token )
541-                               if ( !empty($token) )
542-                                       $objects[] = "attachment:$token";
543-       }
544-
545-       $taxonomies = array();
546-       foreach ( $objects as $object )
547-               if ( $taxes = get_object_taxonomies($object) )
548-                       $taxonomies = array_merge($taxonomies, $taxes);
549-
550-       return array_unique($taxonomies);
551-}
552-
553 function image_attachment_fields_to_edit($form_fields, $post) {
554        if ( substr($post->post_mime_type, 0, 5) == 'image' ) {
555                $form_fields['post_title']['required'] = true;