]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/rewrite.php
Wordpress 2.7.1
[autoinstalls/wordpress.git] / wp-includes / rewrite.php
1 <?php
2 /**
3  * WordPress Rewrite API
4  *
5  * @package WordPress
6  * @subpackage Rewrite
7  */
8
9 /**
10  * Add a straight rewrite rule.
11  *
12  * @see WP_Rewrite::add_rule() for long description.
13  * @since 2.1.0
14  *
15  * @param string $regex Regular Expression to match request against.
16  * @param string $redirect Page to redirect to.
17  * @param string $after Optional, default is 'bottom'. Where to add rule, can also be 'top'.
18  */
19 function add_rewrite_rule($regex, $redirect, $after = 'bottom') {
20         global $wp_rewrite;
21         $wp_rewrite->add_rule($regex, $redirect, $after);
22 }
23
24 /**
25  * Add a new tag (like %postname%).
26  *
27  * Warning: you must call this on init or earlier, otherwise the query var
28  * addition stuff won't work.
29  *
30  * @since 2.1.0
31  *
32  * @param string $tagname
33  * @param string $regex
34  */
35 function add_rewrite_tag($tagname, $regex) {
36         //validation
37         if (strlen($tagname) < 3 || $tagname{0} != '%' || $tagname{strlen($tagname)-1} != '%') {
38                 return;
39         }
40
41         $qv = trim($tagname, '%');
42
43         global $wp_rewrite, $wp;
44         $wp->add_query_var($qv);
45         $wp_rewrite->add_rewrite_tag($tagname, $regex, $qv . '=');
46 }
47
48 /**
49  * Add a new feed type like /atom1/.
50  *
51  * @since 2.1.0
52  *
53  * @param string $feedname
54  * @param callback $function Callback to run on feed display.
55  * @return string Feed action name.
56  */
57 function add_feed($feedname, $function) {
58         global $wp_rewrite;
59         if (!in_array($feedname, $wp_rewrite->feeds)) { //override the file if it is
60                 $wp_rewrite->feeds[] = $feedname;
61         }
62         $hook = 'do_feed_' . $feedname;
63         // Remove default function hook
64         remove_action($hook, $hook, 10, 1);
65         add_action($hook, $function, 10, 1);
66         return $hook;
67 }
68
69 /**
70  * Endpoint Mask for Permalink.
71  *
72  * @since 2.1.0
73  */
74 define('EP_PERMALINK', 1);
75
76 /**
77  * Endpoint Mask for Attachment.
78  *
79  * @since 2.1.0
80  */
81 define('EP_ATTACHMENT', 2);
82
83 /**
84  * Endpoint Mask for date.
85  *
86  * @since 2.1.0
87  */
88 define('EP_DATE', 4);
89
90 /**
91  * Endpoint Mask for year
92  *
93  * @since 2.1.0
94  */
95 define('EP_YEAR', 8);
96
97 /**
98  * Endpoint Mask for month.
99  *
100  * @since 2.1.0
101  */
102 define('EP_MONTH', 16);
103
104 /**
105  * Endpoint Mask for day.
106  *
107  * @since 2.1.0
108  */
109 define('EP_DAY', 32);
110
111 /**
112  * Endpoint Mask for root.
113  *
114  * @since 2.1.0
115  */
116 define('EP_ROOT', 64);
117
118 /**
119  * Endpoint Mask for comments.
120  *
121  * @since 2.1.0
122  */
123 define('EP_COMMENTS', 128);
124
125 /**
126  * Endpoint Mask for searches.
127  *
128  * @since 2.1.0
129  */
130 define('EP_SEARCH', 256);
131
132 /**
133  * Endpoint Mask for categories.
134  *
135  * @since 2.1.0
136  */
137 define('EP_CATEGORIES', 512);
138
139 /**
140  * Endpoint Mask for tags.
141  *
142  * @since 2.3.0
143  */
144 define('EP_TAGS', 1024);
145
146 /**
147  * Endpoint Mask for authors.
148  *
149  * @since 2.1.0
150  */
151 define('EP_AUTHORS', 2048);
152
153 /**
154  * Endpoint Mask for pages.
155  *
156  * @since 2.1.0
157  */
158 define('EP_PAGES', 4096);
159
160 //pseudo-places
161 /**
162  * Endpoint Mask for default, which is nothing.
163  *
164  * @since 2.1.0
165  */
166 define('EP_NONE', 0);
167
168 /**
169  * Endpoint Mask for everything.
170  *
171  * @since 2.1.0
172  */
173 define('EP_ALL', 8191);
174
175 /**
176  * Add an endpoint, like /trackback/.
177  *
178  * The endpoints are added to the end of the request. So a request matching
179  * "/2008/10/14/my_post/myep/", the endpoint will be "/myep/".
180  *
181  * @since 2.1.0
182  * @see WP_Rewrite::add_endpoint() Parameters and more description.
183  * @uses $wp_rewrite
184  *
185  * @param unknown_type $name
186  * @param unknown_type $places
187  */
188 function add_rewrite_endpoint($name, $places) {
189         global $wp_rewrite;
190         $wp_rewrite->add_endpoint($name, $places);
191 }
192
193 /**
194  * Filter the URL base for taxonomies.
195  *
196  * To remove any manually prepended /index.php/.
197  *
198  * @access private
199  * @since 2.6.0
200  * @author Mark Jaquith
201  *
202  * @param string $base The taxonomy base that we're going to filter
203  * @return string
204  */
205 function _wp_filter_taxonomy_base( $base ) {
206         if ( !empty( $base ) ) {
207                 $base = preg_replace( '|^/index\.php/|', '', $base );
208                 $base = trim( $base, '/' );
209         }
210         return $base;
211 }
212
213 /**
214  * Examine a url and try to determine the post ID it represents.
215  *
216  * Checks are supposedly from the hosted site blog.
217  *
218  * @since 1.0.0
219  *
220  * @param string $url Permalink to check.
221  * @return int Post ID, or 0 on failure.
222  */
223 function url_to_postid($url) {
224         global $wp_rewrite;
225
226         $url = apply_filters('url_to_postid', $url);
227
228         // First, check to see if there is a 'p=N' or 'page_id=N' to match against
229         if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) )       {
230                 $id = absint($values[2]);
231                 if ($id)
232                         return $id;
233         }
234
235         // Check to see if we are using rewrite rules
236         $rewrite = $wp_rewrite->wp_rewrite_rules();
237
238         // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
239         if ( empty($rewrite) )
240                 return 0;
241
242         // $url cleanup by Mark Jaquith
243         // This fixes things like #anchors, ?query=strings, missing 'www.',
244         // added 'www.', or added 'index.php/' that will mess up our WP_Query
245         // and return a false negative
246
247         // Get rid of the #anchor
248         $url_split = explode('#', $url);
249         $url = $url_split[0];
250
251         // Get rid of URL ?query=string
252         $url_split = explode('?', $url);
253         $url = $url_split[0];
254
255         // Add 'www.' if it is absent and should be there
256         if ( false !== strpos(get_option('home'), '://www.') && false === strpos($url, '://www.') )
257                 $url = str_replace('://', '://www.', $url);
258
259         // Strip 'www.' if it is present and shouldn't be
260         if ( false === strpos(get_option('home'), '://www.') )
261                 $url = str_replace('://www.', '://', $url);
262
263         // Strip 'index.php/' if we're not using path info permalinks
264         if ( !$wp_rewrite->using_index_permalinks() )
265                 $url = str_replace('index.php/', '', $url);
266
267         if ( false !== strpos($url, get_option('home')) ) {
268                 // Chop off http://domain.com
269                 $url = str_replace(get_option('home'), '', $url);
270         } else {
271                 // Chop off /path/to/blog
272                 $home_path = parse_url(get_option('home'));
273                 $home_path = $home_path['path'];
274                 $url = str_replace($home_path, '', $url);
275         }
276
277         // Trim leading and lagging slashes
278         $url = trim($url, '/');
279
280         $request = $url;
281
282         // Done with cleanup
283
284         // Look for matches.
285         $request_match = $request;
286         foreach ($rewrite as $match => $query) {
287                 // If the requesting file is the anchor of the match, prepend it
288                 // to the path info.
289                 if ( (! empty($url)) && (strpos($match, $url) === 0) && ($url != $request)) {
290                         $request_match = $url . '/' . $request;
291                 }
292
293                 if ( preg_match("!^$match!", $request_match, $matches) ) {
294                         // Got a match.
295                         // Trim the query of everything up to the '?'.
296                         $query = preg_replace("!^.+\?!", '', $query);
297
298                         // Substitute the substring matches into the query.
299                         eval("\$query = \"" . addslashes($query) . "\";");
300                         // Filter out non-public query vars
301                         global $wp;
302                         parse_str($query, $query_vars);
303                         $query = array();
304                         foreach ( (array) $query_vars as $key => $value ) {
305                                 if ( in_array($key, $wp->public_query_vars) )
306                                         $query[$key] = $value;
307                         }
308                         // Do the query
309                         $query = new WP_Query($query);
310                         if ( $query->is_single || $query->is_page )
311                                 return $query->post->ID;
312                         else
313                                 return 0;
314                 }
315         }
316         return 0;
317 }
318
319 /**
320  * WordPress Rewrite Component.
321  *
322  * The WordPress Rewrite class writes the rewrite module rules to the .htaccess
323  * file. It also handles parsing the request to get the correct setup for the
324  * WordPress Query class.
325  *
326  * The Rewrite along with WP class function as a front controller for WordPress.
327  * You can add rules to trigger your page view and processing using this
328  * component. The full functionality of a front controller does not exist,
329  * meaning you can't define how the template files load based on the rewrite
330  * rules.
331  *
332  * @since 1.5.0
333  */
334 class WP_Rewrite {
335         /**
336          * Default permalink structure for WordPress.
337          *
338          * @since 1.5.0
339          * @access private
340          * @var string
341          */
342         var $permalink_structure;
343
344         /**
345          * Whether to add trailing slashes.
346          *
347          * @since 2.2.0
348          * @access private
349          * @var bool
350          */
351         var $use_trailing_slashes;
352
353         /**
354          * Customized or default category permalink base ( example.com/xx/tagname ).
355          *
356          * @since 1.5.0
357          * @access private
358          * @var string
359          */
360         var $category_base;
361
362         /**
363          * Customized or default tag permalink base ( example.com/xx/tagname ).
364          *
365          * @since 2.3.0
366          * @access private
367          * @var string
368          */
369         var $tag_base;
370
371         /**
372          * Permalink request structure for categories.
373          *
374          * @since 1.5.0
375          * @access private
376          * @var string
377          */
378         var $category_structure;
379
380         /**
381          * Permalink request structure for tags.
382          *
383          * @since 2.3.0
384          * @access private
385          * @var string
386          */
387         var $tag_structure;
388
389         /**
390          * Permalink author request base ( example.com/author/authorname ).
391          *
392          * @since 1.5.0
393          * @access private
394          * @var string
395          */
396         var $author_base = 'author';
397
398         /**
399          * Permalink request structure for author pages.
400          *
401          * @since 1.5.0
402          * @access private
403          * @var string
404          */
405         var $author_structure;
406
407         /**
408          * Permalink request structure for dates.
409          *
410          * @since 1.5.0
411          * @access private
412          * @var string
413          */
414         var $date_structure;
415
416         /**
417          * Permalink request structure for pages.
418          *
419          * @since 1.5.0
420          * @access private
421          * @var string
422          */
423         var $page_structure;
424
425         /**
426          * Search permalink base ( example.com/search/query ).
427          *
428          * @since 1.5.0
429          * @access private
430          * @var string
431          */
432         var $search_base = 'search';
433
434         /**
435          * Permalink request structure for searches.
436          *
437          * @since 1.5.0
438          * @access private
439          * @var string
440          */
441         var $search_structure;
442
443         /**
444          * Comments permalink base.
445          *
446          * @since 1.5.0
447          * @access private
448          * @var string
449          */
450         var $comments_base = 'comments';
451
452         /**
453          * Feed permalink base.
454          *
455          * @since 1.5.0
456          * @access private
457          * @var string
458          */
459         var $feed_base = 'feed';
460
461         /**
462          * Comments feed request structure permalink.
463          *
464          * @since 1.5.0
465          * @access private
466          * @var string
467          */
468         var $comments_feed_structure;
469
470         /**
471          * Feed request structure permalink.
472          *
473          * @since 1.5.0
474          * @access private
475          * @var string
476          */
477         var $feed_structure;
478
479         /**
480          * Front URL path.
481          *
482          * The difference between the root property is that WordPress might be
483          * located at example/WordPress/index.php, if permalinks are turned off. The
484          * WordPress/index.php will be the front portion. If permalinks are turned
485          * on, this will most likely be empty or not set.
486          *
487          * @since 1.5.0
488          * @access private
489          * @var string
490          */
491         var $front;
492
493         /**
494          * Root URL path to WordPress (without domain).
495          *
496          * The difference between front property is that WordPress might be located
497          * at example.com/WordPress/. The root is the 'WordPress/' portion.
498          *
499          * @since 1.5.0
500          * @access private
501          * @var string
502          */
503         var $root = '';
504
505         /**
506          * Permalink to the home page.
507          *
508          * @since 1.5.0
509          * @access public
510          * @var string
511          */
512         var $index = 'index.php';
513
514         /**
515          * Request match string.
516          *
517          * @since 1.5.0
518          * @access private
519          * @var string
520          */
521         var $matches = '';
522
523         /**
524          * Rewrite rules to match against the request to find the redirect or query.
525          *
526          * @since 1.5.0
527          * @access private
528          * @var array
529          */
530         var $rules;
531
532         /**
533          * Additional rules added external to the rewrite class.
534          *
535          * Those not generated by the class, see add_rewrite_rule().
536          *
537          * @since 2.1.0
538          * @access private
539          * @var array
540          */
541         var $extra_rules = array(); //
542
543         /**
544          * Additional rules that belong at the beginning to match first.
545          *
546          * Those not generated by the class, see add_rewrite_rule().
547          *
548          * @since 2.3.0
549          * @access private
550          * @var array
551          */
552         var $extra_rules_top = array(); //
553
554         /**
555          * Rules that don't redirect to WP's index.php.
556          *
557          * These rules are written to the mod_rewrite portion of the .htaccess.
558          *
559          * @since 2.1.0
560          * @access private
561          * @var array
562          */
563         var $non_wp_rules = array(); //
564
565         /**
566          * Extra permalink structures.
567          *
568          * @since 2.1.0
569          * @access private
570          * @var array
571          */
572         var $extra_permastructs = array();
573
574         /**
575          * Endpoints permalinks
576          *
577          * @since unknown
578          * @access private
579          * @var array
580          */
581         var $endpoints;
582
583         /**
584          * Whether to write every mod_rewrite rule for WordPress.
585          *
586          * This is off by default, turning it on might print a lot of rewrite rules
587          * to the .htaccess file.
588          *
589          * @since 2.0.0
590          * @access public
591          * @var bool
592          */
593         var $use_verbose_rules = false;
594
595         /**
596          * Whether to write every mod_rewrite rule for WordPress pages.
597          *
598          * @since 2.5.0
599          * @access public
600          * @var bool
601          */
602         var $use_verbose_page_rules = true;
603
604         /**
605          * Permalink structure search for preg_replace.
606          *
607          * @since 1.5.0
608          * @access private
609          * @var array
610          */
611         var $rewritecode =
612                 array(
613                                         '%year%',
614                                         '%monthnum%',
615                                         '%day%',
616                                         '%hour%',
617                                         '%minute%',
618                                         '%second%',
619                                         '%postname%',
620                                         '%post_id%',
621                                         '%category%',
622                                         '%tag%',
623                                         '%author%',
624                                         '%pagename%',
625                                         '%search%'
626                                         );
627
628         /**
629          * Preg_replace values for the search, see {@link WP_Rewrite::$rewritecode}.
630          *
631          * @since 1.5.0
632          * @access private
633          * @var array
634          */
635         var $rewritereplace =
636                 array(
637                                         '([0-9]{4})',
638                                         '([0-9]{1,2})',
639                                         '([0-9]{1,2})',
640                                         '([0-9]{1,2})',
641                                         '([0-9]{1,2})',
642                                         '([0-9]{1,2})',
643                                         '([^/]+)',
644                                         '([0-9]+)',
645                                         '(.+?)',
646                                         '(.+?)',
647                                         '([^/]+)',
648                                         '([^/]+?)',
649                                         '(.+)'
650                                         );
651
652         /**
653          * Search for the query to look for replacing.
654          *
655          * @since 1.5.0
656          * @access private
657          * @var array
658          */
659         var $queryreplace =
660                 array (
661                                         'year=',
662                                         'monthnum=',
663                                         'day=',
664                                         'hour=',
665                                         'minute=',
666                                         'second=',
667                                         'name=',
668                                         'p=',
669                                         'category_name=',
670                                         'tag=',
671                                         'author_name=',
672                                         'pagename=',
673                                         's='
674                                         );
675
676         /**
677          * Supported default feeds.
678          *
679          * @since 1.5.0
680          * @access private
681          * @var array
682          */
683         var $feeds = array ( 'feed', 'rdf', 'rss', 'rss2', 'atom' );
684
685         /**
686          * Whether permalinks are being used.
687          *
688          * This can be either rewrite module or permalink in the HTTP query string.
689          *
690          * @since 1.5.0
691          * @access public
692          *
693          * @return bool True, if permalinks are enabled.
694          */
695         function using_permalinks() {
696                 if (empty($this->permalink_structure))
697                         return false;
698                 else
699                         return true;
700         }
701
702         /**
703          * Whether permalinks are being used and rewrite module is not enabled.
704          *
705          * Means that permalink links are enabled and index.php is in the URL.
706          *
707          * @since 1.5.0
708          * @access public
709          *
710          * @return bool
711          */
712         function using_index_permalinks() {
713                 if (empty($this->permalink_structure)) {
714                         return false;
715                 }
716
717                 // If the index is not in the permalink, we're using mod_rewrite.
718                 if (preg_match('#^/*' . $this->index . '#', $this->permalink_structure)) {
719                         return true;
720                 }
721
722                 return false;
723         }
724
725         /**
726          * Whether permalinks are being used and rewrite module is enabled.
727          *
728          * Using permalinks and index.php is not in the URL.
729          *
730          * @since 1.5.0
731          * @access public
732          *
733          * @return bool
734          */
735         function using_mod_rewrite_permalinks() {
736                 if ( $this->using_permalinks() && ! $this->using_index_permalinks())
737                         return true;
738                 else
739                         return false;
740         }
741
742         /**
743          * Index for matches for usage in preg_*() functions.
744          *
745          * The format of the string is, with empty matches property value, '$NUM'.
746          * The 'NUM' will be replaced with the value in the $number parameter. With
747          * the matches property not empty, the value of the returned string will
748          * contain that value of the matches property. The format then will be
749          * '$MATCHES[NUM]', with MATCHES as the value in the property and NUM the
750          * value of the $number parameter.
751          *
752          * @since 1.5.0
753          * @access public
754          *
755          * @param int $number Index number.
756          * @return string
757          */
758         function preg_index($number) {
759                 $match_prefix = '$';
760                 $match_suffix = '';
761
762                 if ( ! empty($this->matches) ) {
763                         $match_prefix = '$' . $this->matches . '[';
764                         $match_suffix = ']';
765                 }
766
767                 return "$match_prefix$number$match_suffix";
768         }
769
770         /**
771          * Retrieve all page and attachments for pages URIs.
772          *
773          * The attachments are for those that have pages as parents and will be
774          * retrieved.
775          *
776          * @since 2.5.0
777          * @access public
778          *
779          * @return array Array of page URIs as first element and attachment URIs as second element.
780          */
781         function page_uri_index() {
782                 global $wpdb;
783
784                 //get pages in order of hierarchy, i.e. children after parents
785                 $posts = get_page_hierarchy($wpdb->get_results("SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'page'"));
786                 //now reverse it, because we need parents after children for rewrite rules to work properly
787                 $posts = array_reverse($posts, true);
788
789                 $page_uris = array();
790                 $page_attachment_uris = array();
791
792                 if ( !$posts )
793                         return array( array(), array() );
794
795                 foreach ($posts as $id => $post) {
796                         // URL => page name
797                         $uri = get_page_uri($id);
798                         $attachments = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent = %d", $id ));
799                         if ( $attachments ) {
800                                 foreach ( $attachments as $attachment ) {
801                                         $attach_uri = get_page_uri($attachment->ID);
802                                         $page_attachment_uris[$attach_uri] = $attachment->ID;
803                                 }
804                         }
805
806                         $page_uris[$uri] = $id;
807                 }
808
809                 return array( $page_uris, $page_attachment_uris );
810         }
811
812         /**
813          * Retrieve all of the rewrite rules for pages.
814          *
815          * If the 'use_verbose_page_rules' property is false, then there will only
816          * be a single rewrite rule for pages for those matching '%pagename%'. With
817          * the property set to true, the attachments and the pages will be added for
818          * each individual attachment URI and page URI, respectively.
819          *
820          * @since 1.5.0
821          * @access public
822          *
823          * @return array
824          */
825         function page_rewrite_rules() {
826                 $rewrite_rules = array();
827                 $page_structure = $this->get_page_permastruct();
828
829                 if ( ! $this->use_verbose_page_rules ) {
830                         $this->add_rewrite_tag('%pagename%', "(.+?)", 'pagename=');
831                         $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
832                         return $rewrite_rules;
833                 }
834
835                 $page_uris = $this->page_uri_index();
836                 $uris = $page_uris[0];
837                 $attachment_uris = $page_uris[1];
838
839                 if( is_array( $attachment_uris ) ) {
840                         foreach ($attachment_uris as $uri => $pagename) {
841                                 $this->add_rewrite_tag('%pagename%', "($uri)", 'attachment=');
842                                 $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
843                         }
844                 }
845                 if( is_array( $uris ) ) {
846                         foreach ($uris as $uri => $pagename) {
847                                 $this->add_rewrite_tag('%pagename%', "($uri)", 'pagename=');
848                                 $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
849                         }
850                 }
851
852                 return $rewrite_rules;
853         }
854
855         /**
856          * Retrieve date permalink structure, with year, month, and day.
857          *
858          * The permalink structure for the date, if not set already depends on the
859          * permalink structure. It can be one of three formats. The first is year,
860          * month, day; the second is day, month, year; and the last format is month,
861          * day, year. These are matched against the permalink structure for which
862          * one is used. If none matches, then the default will be used, which is
863          * year, month, day.
864          *
865          * Prevents post ID and date permalinks from overlapping. In the case of
866          * post_id, the date permalink will be prepended with front permalink with
867          * 'date/' before the actual permalink to form the complete date permalink
868          * structure.
869          *
870          * @since 1.5.0
871          * @access public
872          *
873          * @return bool|string False on no permalink structure. Date permalink structure.
874          */
875         function get_date_permastruct() {
876                 if (isset($this->date_structure)) {
877                         return $this->date_structure;
878                 }
879
880                 if (empty($this->permalink_structure)) {
881                         $this->date_structure = '';
882                         return false;
883                 }
884
885                 // The date permalink must have year, month, and day separated by slashes.
886                 $endians = array('%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%', '%monthnum%/%day%/%year%');
887
888                 $this->date_structure = '';
889                 $date_endian = '';
890
891                 foreach ($endians as $endian) {
892                         if (false !== strpos($this->permalink_structure, $endian)) {
893                                 $date_endian= $endian;
894                                 break;
895                         }
896                 }
897
898                 if ( empty($date_endian) )
899                         $date_endian = '%year%/%monthnum%/%day%';
900
901                 // Do not allow the date tags and %post_id% to overlap in the permalink
902                 // structure. If they do, move the date tags to $front/date/.
903                 $front = $this->front;
904                 preg_match_all('/%.+?%/', $this->permalink_structure, $tokens);
905                 $tok_index = 1;
906                 foreach ( (array) $tokens[0] as $token) {
907                         if ( ($token == '%post_id%') && ($tok_index <= 3) ) {
908                                 $front = $front . 'date/';
909                                 break;
910                         }
911                         $tok_index++;
912                 }
913
914                 $this->date_structure = $front . $date_endian;
915
916                 return $this->date_structure;
917         }
918
919         /**
920          * Retrieve the year permalink structure without month and day.
921          *
922          * Gets the date permalink structure and strips out the month and day
923          * permalink structures.
924          *
925          * @since 1.5.0
926          * @access public
927          *
928          * @return bool|string False on failure. Year structure on success.
929          */
930         function get_year_permastruct() {
931                 $structure = $this->get_date_permastruct($this->permalink_structure);
932
933                 if (empty($structure)) {
934                         return false;
935                 }
936
937                 $structure = str_replace('%monthnum%', '', $structure);
938                 $structure = str_replace('%day%', '', $structure);
939
940                 $structure = preg_replace('#/+#', '/', $structure);
941
942                 return $structure;
943         }
944
945         /**
946          * Retrieve the month permalink structure without day and with year.
947          *
948          * Gets the date permalink structure and strips out the day permalink
949          * structures. Keeps the year permalink structure.
950          *
951          * @since 1.5.0
952          * @access public
953          *
954          * @return bool|string False on failure. Year/Month structure on success.
955          */
956         function get_month_permastruct() {
957                 $structure = $this->get_date_permastruct($this->permalink_structure);
958
959                 if (empty($structure)) {
960                         return false;
961                 }
962
963                 $structure = str_replace('%day%', '', $structure);
964
965                 $structure = preg_replace('#/+#', '/', $structure);
966
967                 return $structure;
968         }
969
970         /**
971          * Retrieve the day permalink structure with month and year.
972          *
973          * Keeps date permalink structure with all year, month, and day.
974          *
975          * @since 1.5.0
976          * @access public
977          *
978          * @return bool|string False on failure. Year/Month/Day structure on success.
979          */
980         function get_day_permastruct() {
981                 return $this->get_date_permastruct($this->permalink_structure);
982         }
983
984         /**
985          * Retrieve the permalink structure for categories.
986          *
987          * If the category_base property has no value, then the category structure
988          * will have the front property value, followed by 'category', and finally
989          * '%category%'. If it does, then the root property will be used, along with
990          * the category_base property value.
991          *
992          * @since 1.5.0
993          * @access public
994          *
995          * @return bool|string False on failure. Category permalink structure.
996          */
997         function get_category_permastruct() {
998                 if (isset($this->category_structure)) {
999                         return $this->category_structure;
1000                 }
1001
1002                 if (empty($this->permalink_structure)) {
1003                         $this->category_structure = '';
1004                         return false;
1005                 }
1006
1007                 if (empty($this->category_base))
1008                         $this->category_structure = trailingslashit( $this->front . 'category' );
1009                 else
1010                         $this->category_structure = trailingslashit( '/' . $this->root . $this->category_base );
1011
1012                 $this->category_structure .= '%category%';
1013
1014                 return $this->category_structure;
1015         }
1016
1017         /**
1018          * Retrieve the permalink structure for tags.
1019          *
1020          * If the tag_base property has no value, then the tag structure will have
1021          * the front property value, followed by 'tag', and finally '%tag%'. If it
1022          * does, then the root property will be used, along with the tag_base
1023          * property value.
1024          *
1025          * @since 2.3.0
1026          * @access public
1027          *
1028          * @return bool|string False on failure. Tag permalink structure.
1029          */
1030         function get_tag_permastruct() {
1031                 if (isset($this->tag_structure)) {
1032                         return $this->tag_structure;
1033                 }
1034
1035                 if (empty($this->permalink_structure)) {
1036                         $this->tag_structure = '';
1037                         return false;
1038                 }
1039
1040                 if (empty($this->tag_base))
1041                         $this->tag_structure = trailingslashit( $this->front . 'tag' );
1042                 else
1043                         $this->tag_structure = trailingslashit( '/' . $this->root . $this->tag_base );
1044
1045                 $this->tag_structure .= '%tag%';
1046
1047                 return $this->tag_structure;
1048         }
1049
1050         /**
1051          * Retrieve extra permalink structure by name.
1052          *
1053          * @since unknown
1054          * @access public
1055          *
1056          * @param string $name Permalink structure name.
1057          * @return string|bool False if not found. Permalink structure string.
1058          */
1059         function get_extra_permastruct($name) {
1060                 if ( isset($this->extra_permastructs[$name]) )
1061                         return $this->extra_permastructs[$name];
1062                 return false;
1063         }
1064
1065         /**
1066          * Retrieve the author permalink structure.
1067          *
1068          * The permalink structure is front property, author base, and finally
1069          * '/%author%'. Will set the author_structure property and then return it
1070          * without attempting to set the value again.
1071          *
1072          * @since 1.5.0
1073          * @access public
1074          *
1075          * @return string|bool False if not found. Permalink structure string.
1076          */
1077         function get_author_permastruct() {
1078                 if (isset($this->author_structure)) {
1079                         return $this->author_structure;
1080                 }
1081
1082                 if (empty($this->permalink_structure)) {
1083                         $this->author_structure = '';
1084                         return false;
1085                 }
1086
1087                 $this->author_structure = $this->front . $this->author_base . '/%author%';
1088
1089                 return $this->author_structure;
1090         }
1091
1092         /**
1093          * Retrieve the search permalink structure.
1094          *
1095          * The permalink structure is root property, search base, and finally
1096          * '/%search%'. Will set the search_structure property and then return it
1097          * without attempting to set the value again.
1098          *
1099          * @since 1.5.0
1100          * @access public
1101          *
1102          * @return string|bool False if not found. Permalink structure string.
1103          */
1104         function get_search_permastruct() {
1105                 if (isset($this->search_structure)) {
1106                         return $this->search_structure;
1107                 }
1108
1109                 if (empty($this->permalink_structure)) {
1110                         $this->search_structure = '';
1111                         return false;
1112                 }
1113
1114                 $this->search_structure = $this->root . $this->search_base . '/%search%';
1115
1116                 return $this->search_structure;
1117         }
1118
1119         /**
1120          * Retrieve the page permalink structure.
1121          *
1122          * The permalink structure is root property, and '%pagename%'. Will set the
1123          * page_structure property and then return it without attempting to set the
1124          * value again.
1125          *
1126          * @since 1.5.0
1127          * @access public
1128          *
1129          * @return string|bool False if not found. Permalink structure string.
1130          */
1131         function get_page_permastruct() {
1132                 if (isset($this->page_structure)) {
1133                         return $this->page_structure;
1134                 }
1135
1136                 if (empty($this->permalink_structure)) {
1137                         $this->page_structure = '';
1138                         return false;
1139                 }
1140
1141                 $this->page_structure = $this->root . '%pagename%';
1142
1143                 return $this->page_structure;
1144         }
1145
1146         /**
1147          * Retrieve the feed permalink structure.
1148          *
1149          * The permalink structure is root property, feed base, and finally
1150          * '/%feed%'. Will set the feed_structure property and then return it
1151          * without attempting to set the value again.
1152          *
1153          * @since 1.5.0
1154          * @access public
1155          *
1156          * @return string|bool False if not found. Permalink structure string.
1157          */
1158         function get_feed_permastruct() {
1159                 if (isset($this->feed_structure)) {
1160                         return $this->feed_structure;
1161                 }
1162
1163                 if (empty($this->permalink_structure)) {
1164                         $this->feed_structure = '';
1165                         return false;
1166                 }
1167
1168                 $this->feed_structure = $this->root . $this->feed_base . '/%feed%';
1169
1170                 return $this->feed_structure;
1171         }
1172
1173         /**
1174          * Retrieve the comment feed permalink structure.
1175          *
1176          * The permalink structure is root property, comment base property, feed
1177          * base and finally '/%feed%'. Will set the comment_feed_structure property
1178          * and then return it without attempting to set the value again.
1179          *
1180          * @since 1.5.0
1181          * @access public
1182          *
1183          * @return string|bool False if not found. Permalink structure string.
1184          */
1185         function get_comment_feed_permastruct() {
1186                 if (isset($this->comment_feed_structure)) {
1187                         return $this->comment_feed_structure;
1188                 }
1189
1190                 if (empty($this->permalink_structure)) {
1191                         $this->comment_feed_structure = '';
1192                         return false;
1193                 }
1194
1195                 $this->comment_feed_structure = $this->root . $this->comments_base . '/' . $this->feed_base . '/%feed%';
1196
1197                 return $this->comment_feed_structure;
1198         }
1199
1200         /**
1201          * Append or update tag, pattern, and query for replacement.
1202          *
1203          * If the tag already exists, replace the existing pattern and query for
1204          * that tag, otherwise add the new tag, pattern, and query to the end of the
1205          * arrays.
1206          *
1207          * @internal What is the purpose of this function again? Need to finish long
1208          *           description.
1209          *
1210          * @since 1.5.0
1211          * @access public
1212          *
1213          * @param string $tag Append tag to rewritecode property array.
1214          * @param string $pattern Append pattern to rewritereplace property array.
1215          * @param string $query Append query to queryreplace property array.
1216          */
1217         function add_rewrite_tag($tag, $pattern, $query) {
1218                 $position = array_search($tag, $this->rewritecode);
1219                 if ( false !== $position && null !== $position ) {
1220                         $this->rewritereplace[$position] = $pattern;
1221                         $this->queryreplace[$position] = $query;
1222                 } else {
1223                         $this->rewritecode[] = $tag;
1224                         $this->rewritereplace[] = $pattern;
1225                         $this->queryreplace[] = $query;
1226                 }
1227         }
1228
1229         /**
1230          * Generate the rules from permalink structure.
1231          *
1232          * The main WP_Rewrite function for building the rewrite rule list. The
1233          * contents of the function is a mix of black magic and regular expressions,
1234          * so best just ignore the contents and move to the parameters.
1235          *
1236          * @since 1.5.0
1237          * @access public
1238          *
1239          * @param string $permalink_structure The permalink structure.
1240          * @param int $ep_mask Optional, default is EP_NONE. Endpoint constant, see EP_* constants.
1241          * @param bool $paged Optional, default is true. Whether permalink request is paged.
1242          * @param bool $feed Optional, default is true. Whether for feed.
1243          * @param bool $forcomments Optional, default is false. Whether for comments.
1244          * @param bool $walk_dirs Optional, default is true. Whether to create list of directories to walk over.
1245          * @param bool $endpoints Optional, default is true. Whether endpoints are enabled.
1246          * @return array Rewrite rule list.
1247          */
1248         function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) {
1249                 //build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
1250                 $feedregex2 = '';
1251                 foreach ( (array) $this->feeds as $feed_name) {
1252                         $feedregex2 .= $feed_name . '|';
1253                 }
1254                 $feedregex2 = '(' . trim($feedregex2, '|') .  ')/?$';
1255                 //$feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
1256                 //and <permalink>/atom are both possible
1257                 $feedregex = $this->feed_base  . '/' . $feedregex2;
1258
1259                 //build a regex to match the trackback and page/xx parts of URLs
1260                 $trackbackregex = 'trackback/?$';
1261                 $pageregex = 'page/?([0-9]{1,})/?$';
1262                 $commentregex = 'comment-page-([0-9]{1,})/?$';
1263
1264                 //build up an array of endpoint regexes to append => queries to append
1265                 if ($endpoints) {
1266                         $ep_query_append = array ();
1267                         foreach ( (array) $this->endpoints as $endpoint) {
1268                                 //match everything after the endpoint name, but allow for nothing to appear there
1269                                 $epmatch = $endpoint[1] . '(/(.*))?/?$';
1270                                 //this will be appended on to the rest of the query for each dir
1271                                 $epquery = '&' . $endpoint[1] . '=';
1272                                 $ep_query_append[$epmatch] = array ( $endpoint[0], $epquery );
1273                         }
1274                 }
1275
1276                 //get everything up to the first rewrite tag
1277                 $front = substr($permalink_structure, 0, strpos($permalink_structure, '%'));
1278                 //build an array of the tags (note that said array ends up being in $tokens[0])
1279                 preg_match_all('/%.+?%/', $permalink_structure, $tokens);
1280
1281                 $num_tokens = count($tokens[0]);
1282
1283                 $index = $this->index; //probably 'index.php'
1284                 $feedindex = $index;
1285                 $trackbackindex = $index;
1286                 //build a list from the rewritecode and queryreplace arrays, that will look something like
1287                 //tagname=$matches[i] where i is the current $i
1288                 for ($i = 0; $i < $num_tokens; ++$i) {
1289                         if (0 < $i) {
1290                                 $queries[$i] = $queries[$i - 1] . '&';
1291                         } else {
1292                                 $queries[$i] = '';
1293                         }
1294
1295                         $query_token = str_replace($this->rewritecode, $this->queryreplace, $tokens[0][$i]) . $this->preg_index($i+1);
1296                         $queries[$i] .= $query_token;
1297                 }
1298
1299                 //get the structure, minus any cruft (stuff that isn't tags) at the front
1300                 $structure = $permalink_structure;
1301                 if ($front != '/') {
1302                         $structure = str_replace($front, '', $structure);
1303                 }
1304                 //create a list of dirs to walk over, making rewrite rules for each level
1305                 //so for example, a $structure of /%year%/%month%/%postname% would create
1306                 //rewrite rules for /%year%/, /%year%/%month%/ and /%year%/%month%/%postname%
1307                 $structure = trim($structure, '/');
1308                 if ($walk_dirs) {
1309                         $dirs = explode('/', $structure);
1310                 } else {
1311                         $dirs[] = $structure;
1312                 }
1313                 $num_dirs = count($dirs);
1314
1315                 //strip slashes from the front of $front
1316                 $front = preg_replace('|^/+|', '', $front);
1317
1318                 //the main workhorse loop
1319                 $post_rewrite = array();
1320                 $struct = $front;
1321                 for ($j = 0; $j < $num_dirs; ++$j) {
1322                         //get the struct for this dir, and trim slashes off the front
1323                         $struct .= $dirs[$j] . '/'; //accumulate. see comment near explode('/', $structure) above
1324                         $struct = ltrim($struct, '/');
1325                         //replace tags with regexes
1326                         $match = str_replace($this->rewritecode, $this->rewritereplace, $struct);
1327                         //make a list of tags, and store how many there are in $num_toks
1328                         $num_toks = preg_match_all('/%.+?%/', $struct, $toks);
1329                         //get the 'tagname=$matches[i]'
1330                         $query = ( isset($queries) && is_array($queries) ) ? $queries[$num_toks - 1] : '';
1331
1332                         //set up $ep_mask_specific which is used to match more specific URL types
1333                         switch ($dirs[$j]) {
1334                                 case '%year%': $ep_mask_specific = EP_YEAR; break;
1335                                 case '%monthnum%': $ep_mask_specific = EP_MONTH; break;
1336                                 case '%day%': $ep_mask_specific = EP_DAY; break;
1337                         }
1338
1339                         //create query for /page/xx
1340                         $pagematch = $match . $pageregex;
1341                         $pagequery = $index . '?' . $query . '&paged=' . $this->preg_index($num_toks + 1);
1342
1343                         //create query for /comment-page-xx
1344                         $commentmatch = $match . $commentregex;
1345                         $commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index($num_toks + 1);
1346
1347                         //create query for /feed/(feed|atom|rss|rss2|rdf)
1348                         $feedmatch = $match . $feedregex;
1349                         $feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
1350
1351                         //create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex)
1352                         $feedmatch2 = $match . $feedregex2;
1353                         $feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
1354
1355                         //if asked to, turn the feed queries into comment feed ones
1356                         if ($forcomments) {
1357                                 $feedquery .= '&withcomments=1';
1358                                 $feedquery2 .= '&withcomments=1';
1359                         }
1360
1361                         //start creating the array of rewrites for this dir
1362                         $rewrite = array();
1363                         if ($feed) //...adding on /feed/ regexes => queries
1364                                 $rewrite = array($feedmatch => $feedquery, $feedmatch2 => $feedquery2);
1365                         if ($paged) //...and /page/xx ones
1366                                 $rewrite = array_merge($rewrite, array($pagematch => $pagequery));
1367
1368                         //only on pages with comments add ../comment-page-xx/
1369                         if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask || EP_NONE & $ep_mask )
1370                                 $rewrite = array_merge($rewrite, array($commentmatch => $commentquery));
1371
1372                         //do endpoints
1373                         if ($endpoints) {
1374                                 foreach ( (array) $ep_query_append as $regex => $ep) {
1375                                         //add the endpoints on if the mask fits
1376                                         if ($ep[0] & $ep_mask || $ep[0] & $ep_mask_specific) {
1377                                                 $rewrite[$match . $regex] = $index . '?' . $query . $ep[1] . $this->preg_index($num_toks + 2);
1378                                         }
1379                                 }
1380                         }
1381
1382                         //if we've got some tags in this dir
1383                         if ($num_toks) {
1384                                 $post = false;
1385                                 $page = false;
1386
1387                                 //check to see if this dir is permalink-level: i.e. the structure specifies an
1388                                 //individual post. Do this by checking it contains at least one of 1) post name,
1389                                 //2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
1390                                 //minute all present). Set these flags now as we need them for the endpoints.
1391                                 if (strpos($struct, '%postname%') !== false || strpos($struct, '%post_id%') !== false
1392                                                 || strpos($struct, '%pagename%') !== false
1393                                                 || (strpos($struct, '%year%') !== false && strpos($struct, '%monthnum%') !== false && strpos($struct, '%day%') !== false && strpos($struct, '%hour%') !== false && strpos($struct, '%minute%') !== false && strpos($struct, '%second%') !== false)) {
1394                                         $post = true;
1395                                         if (strpos($struct, '%pagename%') !== false)
1396                                                 $page = true;
1397                                 }
1398
1399                                 //if we're creating rules for a permalink, do all the endpoints like attachments etc
1400                                 if ($post) {
1401                                         $post = true;
1402                                         //create query and regex for trackback
1403                                         $trackbackmatch = $match . $trackbackregex;
1404                                         $trackbackquery = $trackbackindex . '?' . $query . '&tb=1';
1405                                         //trim slashes from the end of the regex for this dir
1406                                         $match = rtrim($match, '/');
1407                                         //get rid of brackets
1408                                         $submatchbase = str_replace(array('(',')'),'',$match);
1409
1410                                         //add a rule for at attachments, which take the form of <permalink>/some-text
1411                                         $sub1 = $submatchbase . '/([^/]+)/';
1412                                         $sub1tb = $sub1 . $trackbackregex; //add trackback regex <permalink>/trackback/...
1413                                         $sub1feed = $sub1 . $feedregex; //and <permalink>/feed/(atom|...)
1414                                         $sub1feed2 = $sub1 . $feedregex2; //and <permalink>/(feed|atom...)
1415                                         $sub1comment = $sub1 . $commentregex; //and <permalink>/comment-page-xx
1416                                         //add an ? as we don't have to match that last slash, and finally a $ so we
1417                                         //match to the end of the URL
1418
1419                                         //add another rule to match attachments in the explicit form:
1420                                         //<permalink>/attachment/some-text
1421                                         $sub2 = $submatchbase . '/attachment/([^/]+)/';
1422                                         $sub2tb = $sub2 . $trackbackregex; //and add trackbacks <permalink>/attachment/trackback
1423                                         $sub2feed = $sub2 . $feedregex;    //feeds, <permalink>/attachment/feed/(atom|...)
1424                                         $sub2feed2 = $sub2 . $feedregex2;  //and feeds again on to this <permalink>/attachment/(feed|atom...)
1425                                         $sub2comment = $sub2 . $commentregex; //and <permalink>/comment-page-xx
1426
1427                                         //create queries for these extra tag-ons we've just dealt with
1428                                         $subquery = $index . '?attachment=' . $this->preg_index(1);
1429                                         $subtbquery = $subquery . '&tb=1';
1430                                         $subfeedquery = $subquery . '&feed=' . $this->preg_index(2);
1431                                         $subcommentquery = $subquery . '&cpage=' . $this->preg_index(2);
1432
1433                                         //do endpoints for attachments
1434                                         if ( !empty($endpoint) ) { foreach ( (array) $ep_query_append as $regex => $ep ) {
1435                                                 if ($ep[0] & EP_ATTACHMENT) {
1436                                                         $rewrite[$sub1 . $regex] = $subquery . '?' . $ep[1] . $this->preg_index(2);
1437                                                         $rewrite[$sub2 . $regex] = $subquery . '?' . $ep[1] . $this->preg_index(2);
1438                                                 }
1439                                         } }
1440
1441                                         //now we've finished with endpoints, finish off the $sub1 and $sub2 matches
1442                                         $sub1 .= '?$';
1443                                         $sub2 .= '?$';
1444
1445                                         //allow URLs like <permalink>/2 for <permalink>/page/2
1446                                         $match = $match . '(/[0-9]+)?/?$';
1447                                         $query = $index . '?' . $query . '&page=' . $this->preg_index($num_toks + 1);
1448                                 } else { //not matching a permalink so this is a lot simpler
1449                                         //close the match and finalise the query
1450                                         $match .= '?$';
1451                                         $query = $index . '?' . $query;
1452                                 }
1453
1454                                 //create the final array for this dir by joining the $rewrite array (which currently
1455                                 //only contains rules/queries for trackback, pages etc) to the main regex/query for
1456                                 //this dir
1457                                 $rewrite = array_merge($rewrite, array($match => $query));
1458
1459                                 //if we're matching a permalink, add those extras (attachments etc) on
1460                                 if ($post) {
1461                                         //add trackback
1462                                         $rewrite = array_merge(array($trackbackmatch => $trackbackquery), $rewrite);
1463
1464                                         //add regexes/queries for attachments, attachment trackbacks and so on
1465                                         if ( ! $page ) //require <permalink>/attachment/stuff form for pages because of confusion with subpages
1466                                                 $rewrite = array_merge($rewrite, array($sub1 => $subquery, $sub1tb => $subtbquery, $sub1feed => $subfeedquery, $sub1feed2 => $subfeedquery, $sub1comment => $subcommentquery));
1467                                         $rewrite = array_merge(array($sub2 => $subquery, $sub2tb => $subtbquery, $sub2feed => $subfeedquery, $sub2feed2 => $subfeedquery, $sub2comment => $subcommentquery), $rewrite);
1468                                 }
1469                         } //if($num_toks)
1470                         //add the rules for this dir to the accumulating $post_rewrite
1471                         $post_rewrite = array_merge($rewrite, $post_rewrite);
1472                 } //foreach ($dir)
1473                 return $post_rewrite; //the finished rules. phew!
1474         }
1475
1476         /**
1477          * Generate Rewrite rules with permalink structure and walking directory only.
1478          *
1479          * Shorten version of {@link WP_Rewrite::generate_rewrite_rules()} that
1480          * allows for shorter list of parameters. See the method for longer
1481          * description of what generating rewrite rules does.
1482          *
1483          * @uses WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters.
1484          * @since 1.5.0
1485          * @access public
1486          *
1487          * @param string $permalink_structure The permalink structure to generate rules.
1488          * @param bool $walk_dirs Optional, default is false. Whether to create list of directories to walk over.
1489          * @return array
1490          */
1491         function generate_rewrite_rule($permalink_structure, $walk_dirs = false) {
1492                 return $this->generate_rewrite_rules($permalink_structure, EP_NONE, false, false, false, $walk_dirs);
1493         }
1494
1495         /**
1496          * Construct rewrite matches and queries from permalink structure.
1497          *
1498          * Runs the action 'generate_rewrite_rules' with the parameter that is an
1499          * reference to the current WP_Rewrite instance to further manipulate the
1500          * permalink structures and rewrite rules. Runs the 'rewrite_rules_array'
1501          * filter on the full rewrite rule array.
1502          *
1503          * There are two ways to manipulate the rewrite rules, one by hooking into
1504          * the 'generate_rewrite_rules' action and gaining full control of the
1505          * object or just manipulating the rewrite rule array before it is passed
1506          * from the function.
1507          *
1508          * @since 1.5.0
1509          * @access public
1510          *
1511          * @return array An associate array of matches and queries.
1512          */
1513         function rewrite_rules() {
1514                 $rewrite = array();
1515
1516                 if (empty($this->permalink_structure)) {
1517                         return $rewrite;
1518                 }
1519
1520                 // robots.txt
1521                 $robots_rewrite = array('robots\.txt$' => $this->index . '?robots=1');
1522
1523                 //Default Feed rules - These are require to allow for the direct access files to work with permalink structure starting with %category%
1524                 $default_feeds = array( '.*wp-atom.php$'        =>      $this->index .'?feed=atom',
1525                                                                 '.*wp-rdf.php$' =>      $this->index .'?feed=rdf',
1526                                                                 '.*wp-rss.php$' =>      $this->index .'?feed=rss',
1527                                                                 '.*wp-rss2.php$'        =>      $this->index .'?feed=rss2',
1528                                                                 '.*wp-feed.php$'        =>      $this->index .'?feed=feed',
1529                                                                 '.*wp-commentsrss2.php$'        =>      $this->index . '?feed=rss2&withcomments=1');
1530
1531                 // Post
1532                 $post_rewrite = $this->generate_rewrite_rules($this->permalink_structure, EP_PERMALINK);
1533                 $post_rewrite = apply_filters('post_rewrite_rules', $post_rewrite);
1534
1535                 // Date
1536                 $date_rewrite = $this->generate_rewrite_rules($this->get_date_permastruct(), EP_DATE);
1537                 $date_rewrite = apply_filters('date_rewrite_rules', $date_rewrite);
1538
1539                 // Root
1540                 $root_rewrite = $this->generate_rewrite_rules($this->root . '/', EP_ROOT);
1541                 $root_rewrite = apply_filters('root_rewrite_rules', $root_rewrite);
1542
1543                 // Comments
1544                 $comments_rewrite = $this->generate_rewrite_rules($this->root . $this->comments_base, EP_COMMENTS, true, true, true, false);
1545                 $comments_rewrite = apply_filters('comments_rewrite_rules', $comments_rewrite);
1546
1547                 // Search
1548                 $search_structure = $this->get_search_permastruct();
1549                 $search_rewrite = $this->generate_rewrite_rules($search_structure, EP_SEARCH);
1550                 $search_rewrite = apply_filters('search_rewrite_rules', $search_rewrite);
1551
1552                 // Categories
1553                 $category_rewrite = $this->generate_rewrite_rules($this->get_category_permastruct(), EP_CATEGORIES);
1554                 $category_rewrite = apply_filters('category_rewrite_rules', $category_rewrite);
1555
1556                 // Tags
1557                 $tag_rewrite = $this->generate_rewrite_rules($this->get_tag_permastruct(), EP_TAGS);
1558                 $tag_rewrite = apply_filters('tag_rewrite_rules', $tag_rewrite);
1559
1560                 // Authors
1561                 $author_rewrite = $this->generate_rewrite_rules($this->get_author_permastruct(), EP_AUTHORS);
1562                 $author_rewrite = apply_filters('author_rewrite_rules', $author_rewrite);
1563
1564                 // Pages
1565                 $page_rewrite = $this->page_rewrite_rules();
1566                 $page_rewrite = apply_filters('page_rewrite_rules', $page_rewrite);
1567
1568                 // Extra permastructs
1569                 foreach ( $this->extra_permastructs as $permastruct )
1570                         $this->extra_rules_top = array_merge($this->extra_rules_top, $this->generate_rewrite_rules($permastruct, EP_NONE));
1571
1572                 // Put them together.
1573                 if ( $this->use_verbose_page_rules )
1574                         $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);
1575                 else
1576                         $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);
1577
1578                 do_action_ref_array('generate_rewrite_rules', array(&$this));
1579                 $this->rules = apply_filters('rewrite_rules_array', $this->rules);
1580
1581                 return $this->rules;
1582         }
1583
1584         /**
1585          * Retrieve the rewrite rules.
1586          *
1587          * The difference between this method and {@link
1588          * WP_Rewrite::rewrite_rules()} is that this method stores the rewrite rules
1589          * in the 'rewrite_rules' option and retrieves it. This prevents having to
1590          * process all of the permalinks to get the rewrite rules in the form of
1591          * caching.
1592          *
1593          * @since 1.5.0
1594          * @access public
1595          *
1596          * @return array Rewrite rules.
1597          */
1598         function wp_rewrite_rules() {
1599                 $this->rules = get_option('rewrite_rules');
1600                 if ( empty($this->rules) ) {
1601                         $this->matches = 'matches';
1602                         $this->rewrite_rules();
1603                         update_option('rewrite_rules', $this->rules);
1604                 }
1605
1606                 return $this->rules;
1607         }
1608
1609         /**
1610          * Retrieve mod_rewrite formatted rewrite rules to write to .htaccess.
1611          *
1612          * Does not actually write to the .htaccess file, but creates the rules for
1613          * the process that will.
1614          *
1615          * Will add  the non_wp_rules property rules to the .htaccess file before
1616          * the WordPress rewrite rules one.
1617          *
1618          * @since 1.5.0
1619          * @access public
1620          *
1621          * @return string
1622          */
1623         function mod_rewrite_rules() {
1624                 if ( ! $this->using_permalinks()) {
1625                         return '';
1626                 }
1627
1628                 $site_root = parse_url(get_option('siteurl'));
1629                 if ( isset( $site_root['path'] ) ) {
1630                         $site_root = trailingslashit($site_root['path']);
1631                 }
1632
1633                 $home_root = parse_url(get_option('home'));
1634                 if ( isset( $home_root['path'] ) ) {
1635                         $home_root = trailingslashit($home_root['path']);
1636                 } else {
1637                         $home_root = '/';
1638                 }
1639
1640                 $rules = "<IfModule mod_rewrite.c>\n";
1641                 $rules .= "RewriteEngine On\n";
1642                 $rules .= "RewriteBase $home_root\n";
1643
1644                 //add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all)
1645                 foreach ( (array) $this->non_wp_rules as $match => $query) {
1646                         // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1647                         $match = str_replace('.+?', '.+', $match);
1648
1649                         // If the match is unanchored and greedy, prepend rewrite conditions
1650                         // to avoid infinite redirects and eclipsing of real files.
1651                         if ($match == '(.+)/?$' || $match == '([^/]+)/?$' ) {
1652                                 //nada.
1653                         }
1654
1655                         $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1656                 }
1657
1658                 if ($this->use_verbose_rules) {
1659                         $this->matches = '';
1660                         $rewrite = $this->rewrite_rules();
1661                         $num_rules = count($rewrite);
1662                         $rules .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
1663                                 "RewriteCond %{REQUEST_FILENAME} -d\n" .
1664                                 "RewriteRule ^.*$ - [S=$num_rules]\n";
1665
1666                         foreach ( (array) $rewrite as $match => $query) {
1667                                 // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1668                                 $match = str_replace('.+?', '.+', $match);
1669
1670                                 // If the match is unanchored and greedy, prepend rewrite conditions
1671                                 // to avoid infinite redirects and eclipsing of real files.
1672                                 if ($match == '(.+)/?$' || $match == '([^/]+)/?$' ) {
1673                                         //nada.
1674                                 }
1675
1676                                 if (strpos($query, $this->index) !== false) {
1677                                         $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1678                                 } else {
1679                                         $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
1680                                 }
1681                         }
1682                 } else {
1683                         $rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
1684                                 "RewriteCond %{REQUEST_FILENAME} !-d\n" .
1685                                 "RewriteRule . {$home_root}{$this->index} [L]\n";
1686                 }
1687
1688                 $rules .= "</IfModule>\n";
1689
1690                 $rules = apply_filters('mod_rewrite_rules', $rules);
1691                 $rules = apply_filters('rewrite_rules', $rules);  // Deprecated
1692
1693                 return $rules;
1694         }
1695
1696         /**
1697          * Add a straight rewrite rule.
1698          *
1699          * Any value in the $after parameter that isn't 'bottom' will be placed at
1700          * the top of the rules.
1701          *
1702          * @since 2.1.0
1703          * @access public
1704          *
1705          * @param string $regex Regular expression to match against request.
1706          * @param string $redirect URL regex redirects to when regex matches request.
1707          * @param string $after Optional, default is bottom. Location to place rule.
1708          */
1709         function add_rule($regex, $redirect, $after = 'bottom') {
1710                 //get everything up to the first ?
1711                 $index = (strpos($redirect, '?') == false ? strlen($redirect) : strpos($redirect, '?'));
1712                 $front = substr($redirect, 0, $index);
1713                 if ($front != $this->index) { //it doesn't redirect to WP's index.php
1714                         $this->add_external_rule($regex, $redirect);
1715                 } else {
1716                         if ( 'bottom' == $after)
1717                                 $this->extra_rules = array_merge($this->extra_rules, array($regex => $redirect));
1718                         else
1719                                 $this->extra_rules_top = array_merge($this->extra_rules_top, array($regex => $redirect));
1720                         //$this->extra_rules[$regex] = $redirect;
1721                 }
1722         }
1723
1724         /**
1725          * Add a rule that doesn't redirect to index.php.
1726          *
1727          * Can redirect to any place.
1728          *
1729          * @since 2.1.0
1730          * @access public
1731          *
1732          * @param string $regex Regular expression to match against request.
1733          * @param string $redirect URL regex redirects to when regex matches request.
1734          */
1735         function add_external_rule($regex, $redirect) {
1736                 $this->non_wp_rules[$regex] = $redirect;
1737         }
1738
1739         /**
1740          * Add an endpoint, like /trackback/.
1741          *
1742          * To be inserted after certain URL types (specified in $places).
1743          *
1744          * @since 2.1.0
1745          * @access public
1746          *
1747          * @param string $name Name of endpoint.
1748          * @param array $places URL types that endpoint can be used.
1749          */
1750         function add_endpoint($name, $places) {
1751                 global $wp;
1752                 $this->endpoints[] = array ( $places, $name );
1753                 $wp->add_query_var($name);
1754         }
1755
1756         /**
1757          * Add permalink structure.
1758          *
1759          * These are added along with the extra rewrite rules that are merged to the
1760          * top.
1761          *
1762          * @since unknown
1763          * @access public
1764          *
1765          * @param string $name Name for permalink structure.
1766          * @param string $struct Permalink structure.
1767          * @param bool $with_front Prepend front base to permalink structure.
1768          */
1769         function add_permastruct($name, $struct, $with_front = true) {
1770                 if ( $with_front )
1771                         $struct = $this->front . $struct;
1772                 $this->extra_permastructs[$name] = $struct;
1773         }
1774
1775         /**
1776          * Remove rewrite rules and then recreate rewrite rules.
1777          *
1778          * Calls {@link WP_Rewrite::wp_rewrite_rules()} after removing the
1779          * 'rewrite_rules' option. If the function named 'save_mod_rewrite_rules'
1780          * exists, it will be called.
1781          *
1782          * @since 2.0.1
1783          * @access public
1784          */
1785         function flush_rules() {
1786                 delete_option('rewrite_rules');
1787                 $this->wp_rewrite_rules();
1788                 if ( function_exists('save_mod_rewrite_rules') )
1789                         save_mod_rewrite_rules();
1790         }
1791
1792         /**
1793          * Sets up the object's properties.
1794          *
1795          * The 'use_verbose_page_rules' object property will be turned on, if the
1796          * permalink structure includes the following: '%postname%', '%category%',
1797          * '%tag%', or '%author%'.
1798          *
1799          * @since 1.5.0
1800          * @access public
1801          */
1802         function init() {
1803                 $this->extra_rules = $this->non_wp_rules = $this->endpoints = array();
1804                 $this->permalink_structure = get_option('permalink_structure');
1805                 $this->front = substr($this->permalink_structure, 0, strpos($this->permalink_structure, '%'));
1806                 $this->root = '';
1807                 if ($this->using_index_permalinks()) {
1808                         $this->root = $this->index . '/';
1809                 }
1810                 $this->category_base = get_option( 'category_base' );
1811                 $this->tag_base = get_option( 'tag_base' );
1812                 unset($this->category_structure);
1813                 unset($this->author_structure);
1814                 unset($this->date_structure);
1815                 unset($this->page_structure);
1816                 unset($this->search_structure);
1817                 unset($this->feed_structure);
1818                 unset($this->comment_feed_structure);
1819                 $this->use_trailing_slashes = ( substr($this->permalink_structure, -1, 1) == '/' ) ? true : false;
1820
1821                 // Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
1822                 $structure = ltrim($this->permalink_structure, '/');
1823                 if ( $this->using_index_permalinks() )
1824                         $structure = ltrim($this->permalink_structure, $this->index . '/');
1825                 if ( 0 === strpos($structure, '%postname%') ||
1826                          0 === strpos($structure, '%category%') ||
1827                          0 === strpos($structure, '%tag%') ||
1828                          0 === strpos($structure, '%author%') )
1829                          $this->use_verbose_page_rules = true;
1830                 else
1831                         $this->use_verbose_page_rules = false;
1832         }
1833
1834         /**
1835          * Set the main permalink structure for the blog.
1836          *
1837          * Will update the 'permalink_structure' option, if there is a difference
1838          * between the current permalink structure and the parameter value. Calls
1839          * {@link WP_Rewrite::init()} after the option is updated.
1840          *
1841          * @since 1.5.0
1842          * @access public
1843          *
1844          * @param string $permalink_structure Permalink structure.
1845          */
1846         function set_permalink_structure($permalink_structure) {
1847                 if ($permalink_structure != $this->permalink_structure) {
1848                         update_option('permalink_structure', $permalink_structure);
1849                         $this->init();
1850                 }
1851         }
1852
1853         /**
1854          * Set the category base for the category permalink.
1855          *
1856          * Will update the 'category_base' option, if there is a difference between
1857          * the current category base and the parameter value. Calls
1858          * {@link WP_Rewrite::init()} after the option is updated.
1859          *
1860          * @since 1.5.0
1861          * @access public
1862          *
1863          * @param string $category_base Category permalink structure base.
1864          */
1865         function set_category_base($category_base) {
1866                 if ($category_base != $this->category_base) {
1867                         update_option('category_base', $category_base);
1868                         $this->init();
1869                 }
1870         }
1871
1872         /**
1873          * Set the tag base for the tag permalink.
1874          *
1875          * Will update the 'tag_base' option, if there is a difference between the
1876          * current tag base and the parameter value. Calls
1877          * {@link WP_Rewrite::init()} after the option is updated.
1878          *
1879          * @since 2.3.0
1880          * @access public
1881          *
1882          * @param string $tag_base Tag permalink structure base.
1883          */
1884         function set_tag_base( $tag_base ) {
1885                 if ( $tag_base != $this->tag_base ) {
1886                         update_option( 'tag_base', $tag_base );
1887                         $this->init();
1888                 }
1889         }
1890
1891         /**
1892          * PHP4 Constructor - Calls init(), which runs setup.
1893          *
1894          * @since 1.5.0
1895          * @access public
1896          *
1897          * @return WP_Rewrite
1898          */
1899         function WP_Rewrite() {
1900                 $this->init();
1901         }
1902 }
1903
1904 ?>