Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

ADDED: Ability to configure the resulting url a lot better. Attempted…

… to create both a route and url generator system that works from a single string pattern, although it may not be very robust.

FIXED: phpdoc comments are a lot more complete
ADDED: Filter function for class registration
  • Loading branch information...
commit 6474b4be72c6d6dbf4cead259ff5ddb3438c8f3c 1 parent ef9f27b
authored March 01, 2012
77  code/DateLink.php
@@ -2,12 +2,38 @@
@@ -23,34 +49,61 @@ class DateLink
5  code/Decorators/DateLinkHolderDecorator.php
... ...
@@ -1,9 +1,8 @@
1 1
 <?php
2 2
 
3 3
 /**
4  
- * Decorates the holder page of any dated-linked page
5  
- *
6  
- * @author Damo
  4
+ * Allows the date link cache to update during dev/build
  5
+ * @author Damian Mooyman
7 6
  */
8 7
 class DateLinkHolderDecorator extends SiteTreeDecorator
9 8
 {
67  code/Decorators/DateLinkPageDecorator.php
... ...
@@ -1,9 +1,8 @@
1 1
 <?php
2 2
 
3 3
 /**
4  
- * Description of DateLinkPageDecorator
5  
- *
6  
- * @author Damo
  4
+ * Provides functionality required for date-linked pages to generate urls and to refresh the cache
  5
+ * @author Damian Mooyman
7 6
  */
8 7
 class DateLinkPageDecorator extends SiteTreeDecorator
9 8
 {
@@ -14,24 +13,62 @@ public function onAfterWrite()
14 13
         DateLink::refresh_cache();
15 14
     }
16 15
 
  16
+    /**
  17
+     * Using the same rules that were used to generate the routing table, attempt to generate the original matching
  18
+     * url that corresponds to the current page
  19
+     * @param string|null $action The requested page action
  20
+     * @return string the relative page url
  21
+     */
17 22
     public function DatedRelativeLink($action = null)
18 23
     {
  24
+        // Fixes the "please use full link" hack that ContentController::link uses to request full parent controller links.
  25
+        if ($action === true)
  26
+            $action = null;
  27
+
19 28
         $parentLink = $this->owner->Parent()->RelativeLink();
20  
-        $URLSegment = $this->owner->URLSegment;
21  
-        $date = $this->owner->getField(DateLink::get_date_field());
  29
+        $urlSegment = $this->owner->URLSegment;
22 30
         
23  
-        // Fixes the "please use full link" hack that ContentController::link uses to request full page links.
24  
-        if($action === true)
25  
-            $action = null;
  31
+        // Determine raw date
  32
+        $fieldName = DateLink::get_date_field();
  33
+        $dateValue = $this->owner->$fieldName;
  34
+
  35
+        // Handles the outlying case where this page has no date set, in which case we revert to a quick 
  36
+        // implementation of the traditional RelativeLink function
  37
+        if (!$dateValue)
  38
+            return Controller::join_links($parentLink, $urlSegment, '/', $action);
  39
+        $date = new DateTime($dateValue);
  40
+        
  41
+        // Get pattern to use as a base for this link
  42
+        $pattern = DateLink::get_url_pattern();
  43
+
  44
+        // Substitute parent link
  45
+        $parentLink = trim($parentLink, "/");
  46
+        $pattern = preg_replace('/\$ParentLink!?/i', $parentLink, $pattern);
26 47
 
27  
-        if (!$date)
28  
-            return Controller::join_links($parentLink, $URLSegment, '/',  $action);
  48
+        // Substitute parent segment
  49
+        $pattern = preg_replace('/\$URLSegment!?/i', $urlSegment, $pattern);
  50
+        
  51
+        // Mapping of wildcards to date formats (in order of replacement)
  52
+        $replacements = array(
  53
+            '#$Month' => 'm',
  54
+            '$MonthName' => 'F',
  55
+            '$Month' => 'n',
  56
+            '#$Date' => 'd',
  57
+            '$Date' => 'j',
  58
+            '$Year' => 'Y',
  59
+            '$Weekday' => 'l'
  60
+        );
29 61
 
30  
-        // Merge all elements together
31  
-        $parsedDate = date_parse($date);
32  
-        $year = $parsedDate['year'];
33  
-        $month = $parsedDate['month'];
34  
-        return Controller::join_links($parentLink, $year, $month, $URLSegment, '/', $action);
  62
+        // Extract various date elements
  63
+        foreach ($replacements as $search => $dateFormat)
  64
+        {
  65
+            $dateText = $date->format($dateFormat);
  66
+            $quotedSearch = preg_quote($search);
  67
+            $pattern = preg_replace("/$quotedSearch!?/i", $dateText, $pattern);
  68
+        }
  69
+        
  70
+        // Replace everything after the // with /$action
  71
+        return preg_replace('/\/\/.*$/', "/$action", $pattern);
35 72
     }
36 73
 
37 74
 }
3  code/Routing/DateLinkController.php
@@ -2,8 +2,7 @@
2 2
 
3 3
 /**
4 4
  * Handles urls that match dated link routes
5  
- *
6  
- * @author Damo
  5
+ * @author Damian Mooyman
7 6
  * @see ModelAsController
8 7
  */
9 8
 class DateLinkController extends ModelAsController
140  code/Routing/DateLinkRouter.php
... ...
@@ -1,35 +1,66 @@
1 1
 <?php
2 2
 
3  
-/*
4  
- * To change this template, choose Tools | Templates
5  
- * and open the template in the editor.
6  
- */
7  
-
8 3
 /**
9  
- * Description of DateLinkRouter
10  
- *
11  
- * @author Damo
  4
+ * Handles all routing pattern setup within the Silverstripe routing tables
  5
+ * @author Damian Mooyman
12 6
  */
13 7
 class DateLinkRouter implements IDateLinkRouter
14 8
 {
  9
+    /**
  10
+     * Name of database field to use when extracting dates from child pages.
  11
+     * Currently this is restricted to a single value, but you could easily create a wrapper for this in your code
  12
+     * with a getURLDate function and set it to this.
  13
+     * @var string
  14
+     */
15 15
     protected $dateField;
  16
+    
  17
+    /**
  18
+     * Pattern to use when building as well as routing links. Using one variable for both purposes keeps things consistent
  19
+     * @see DateLink::$default_url_pattern
  20
+     * @var string
  21
+     */
  22
+    protected $urlPattern;
  23
+    
  24
+    /**
  25
+     * List of class names that have been registered as parent pages of date-mapped urls
  26
+     * @var array
  27
+     */
16 28
     protected $classNames = array();
  29
+    
  30
+    /**
  31
+     * Class name to use for implementation of the controller class that will handle all routed requests
  32
+     * Should be a subclass of Controller
  33
+     * @see Controller
  34
+     * @var string
  35
+     */
17 36
     protected $controllerClass = 'DateLinkController';
18 37
 
19 38
     /**
20  
-     * File used to store routing table
  39
+     * Filename used to store routing table
21 40
      * This is required as database access is not accessible during routing, so these tables must be
22  
-     * built during dev/build and read during page init
23  
-     * @var string routing
  41
+     * built during dev/build and read during page init.
  42
+     * This file will be created under assets folder as it's a writable file, and I don't by policy put
  43
+     * writable files outside of this location.
  44
+     * @var string
24 45
      */
25 46
     protected $routeCache = '_datelink/Routing.xml';
26 47
 
27  
-    public function __construct($dateField)
  48
+    /**
  49
+     * Construct this instance of the routing handler
  50
+     * @param string $dateField Name of database field to use when extracting dates from child pages.
  51
+     * @param string $urlPattern
  52
+     */
  53
+    public function __construct($dateField, $urlPattern)
28 54
     {
29 55
         $this->dateField = $dateField;
  56
+        $this->urlPattern = $urlPattern;
30 57
         $this->init();
31 58
     }
32 59
 
  60
+    /**
  61
+     * Sets up this module. By default, if this module is never referenced in user code nothing is ever constructed.
  62
+     * This is used as a trigger to flag when this module is enabled, rather than by explicitly calling setup somewhere.
  63
+     */
33 64
     protected function init()
34 65
     {
35 66
         // Automatically decorates pages with required helper functions
@@ -41,19 +72,51 @@ protected function init()
41 72
         $this->registerRoutes();
42 73
     }
43 74
 
  75
+    /**
  76
+     * Sets the field to extract the date from
  77
+     * @param string $field Name of the date field
  78
+     */
44 79
     public function setDateField($field)
45 80
     {
46 81
         $this->dateField = $field;
47 82
     }
48 83
 
  84
+    /**
  85
+     * Gets the field to extract the date from
  86
+     * @return string Name of the date field
  87
+     */
49 88
     public function getDateField()
50 89
     {
51 90
         return $this->dateField;
52 91
     }
53 92
 
54  
-    public function RegisterClass($className)
  93
+    /**
  94
+     * Sets the pattern to use when routing and generating links
  95
+     * @param string $pattern The pattern to use
  96
+     */
  97
+    public function setURLPattern($pattern)
55 98
     {
56  
-        $this->classNames[] = $className;
  99
+        $this->urlPattern = $pattern;
  100
+    }
  101
+
  102
+    /**
  103
+     * Gets the pattern to use when routing and generating links
  104
+     * @return string The pattern used
  105
+     */
  106
+    public function getURLPattern()
  107
+    {
  108
+        return $this->urlPattern;
  109
+    }
  110
+
  111
+    /**
  112
+     * Registers a class beneath which all child pages will be date-mapped.
  113
+     * @param type $className Name of the class to register
  114
+     * @param callback|boolean $filter a boolean, callback, or other closure that can be passed instances of the
  115
+     * specified class to determine if it should be consitered when filtering
  116
+     */
  117
+    public function RegisterClass($className, $filter = true)
  118
+    {
  119
+        $this->classNames[$className] = $filter;
57 120
         DataObject::add_extension($className, 'DateLinkHolderDecorator');
58 121
     }
59 122
 
@@ -84,16 +147,29 @@ protected function registerRoutes()
84 147
         }
85 148
     }
86 149
 
87  
-    protected function registerRoute($link, $parentID, $yearNumber)
  150
+    /**
  151
+     * Registers a single routing rule within silverstripe
  152
+     * @param string $parentLink The link of the parent page
  153
+     * @param integer $parentID The ID of the parent page
  154
+     * @param integer $yearNumber The value of the $Year parameter
  155
+     */
  156
+    protected function registerRoute($parentLink, $parentID, $yearNumber)
88 157
     {
89  
-        $trimmedLink = trim($link, '/');
90  
-        if(!empty($trimmedLink))
91  
-            $trimmedLink .= '/'; // considers situation where we could be using the home page
92  
-        $pattern = '$Month!/$URLSegment!//$Action/$ID/$OtherID';
93  
-        $fullPattern = "$trimmedLink$yearNumber/$pattern";
  158
+        $parentLink = trim($parentLink, '/');
  159
+        $pattern = $this->urlPattern;
  160
+        
  161
+        // Replace wildcards in pattern
  162
+        // Substitute leading-zero indicators
  163
+        $pattern = preg_replace('/#\$/', '$', $pattern);
  164
+        // substitute the year
  165
+        $pattern = preg_replace('/\$Year!?/i', $yearNumber, $pattern);
  166
+        // substitute parent url
  167
+        $pattern = preg_replace('/\$ParentLink!?/', $parentLink, $pattern);
  168
+        // Fix any extra slashes, which may occur if the $parentLink is /
  169
+        $pattern = trim($pattern, '/');
94 170
         
95  
-        // Registers a route with
96  
-        Director::addRules(20,array($fullPattern => array(
  171
+        // Registers a route with silverstripe
  172
+        Director::addRules(20,array($pattern => array(
97 173
             'Controller' => $this->controllerClass,
98 174
             'ParentID' => $parentID, // Used as a shortcut for simplifying nested routing
99 175
             'Year' => $yearNumber
@@ -123,18 +199,30 @@ protected function determineYears(DataObjectSet $pages)
123 199
         return array_unique($years);
124 200
     }
125 201
 
  202
+    /**
  203
+     * Instructs the module to refresh the routing XML cache file
  204
+     * This may not be called during manifest initialisation (_config.php) as database access is not available
  205
+     */
126 206
     public function RefreshCache()
127 207
     {
128  
-// Builds XML cache file using all available routes
  208
+        // Builds XML cache file using all available routes
129 209
         $document = new DOMDocument();
130 210
         $document->formatOutput = true;
131 211
         $routes = $document->createElement('routes');
132 212
         $document->appendChild($routes);
133 213
 
134  
-// append all routes
135  
-        foreach ($this->classNames as $className)
  214
+        // append all routes
  215
+        foreach ($this->classNames as $className => $filter)
136 216
             foreach (DataObject::get($className) as $holderPage)
137 217
             {
  218
+                // Apply any specified filter
  219
+                if(is_callable($filter) && !call_user_func($filter, $holderPage))
  220
+                    continue;
  221
+                
  222
+                // Fallback to using boolean filter
  223
+                if(!$filter)
  224
+                    continue;
  225
+            
138 226
                 $route = $document->createElement('route');
139 227
                 $link = $document->createElement('link', $holderPage->Link());
140 228
                 $route->appendChild($link);
@@ -157,7 +245,7 @@ public function RefreshCache()
157 245
                 $routes->appendChild($route);
158 246
             }
159 247
 
160  
-// Ensure output directory exists
  248
+        // Ensure output directory exists
161 249
         $outputPath = $this->getCacheFilePath();
162 250
 
163 251
         Filesystem::makeFolder(dirname($outputPath));
36  interfaces/IDateLinkRouter.php
... ...
@@ -1,16 +1,46 @@
1 1
 <?php
2 2
 
3 3
 /**
4  
- *
5  
- * @author Damo
  4
+ * Interface for the date link route handler
  5
+ * @author Damian Mooyman
6 6
  */
7 7
 interface IDateLinkRouter
8 8
 {
  9
+        /**
  10
+     * Sets the field to extract the date from
  11
+     * @param string $field Name of the date field
  12
+     */
9 13
     public function setDateField($field);
10 14
 
  15
+    /**
  16
+     * Gets the field to extract the date from
  17
+     * @return string Name of the date field
  18
+     */
11 19
     public function getDateField();
12 20
 
13  
-    public function RegisterClass($className);
  21
+    /**
  22
+     * Sets the pattern to use when routing and generating links
  23
+     * @param string $pattern The pattern to use
  24
+     */
  25
+    public function setURLPattern($pattern);
  26
+
  27
+    /**
  28
+     * Gets the pattern to use when routing and generating links
  29
+     * @return string The pattern used
  30
+     */
  31
+    public function getURLPattern();
  32
+
  33
+    /**
  34
+     * Registers a class beneath which all child pages will be date-mapped.
  35
+     * @param type $className Name of the class to register
  36
+     * @param callback|boolean $filter a boolean, callback, or other closure that can be passed instances of the
  37
+     * specified class to determine if it should be consitered when filtering
  38
+     */
  39
+    public function RegisterClass($className, $filter = true);
14 40
     
  41
+    /**
  42
+     * Instructs the module to refresh the routing XML cache file
  43
+     * This may not be called during manifest initialisation (_config.php) as database access is not available
  44
+     */
15 45
     public function RefreshCache();
16 46
 }

0 notes on commit 6474b4b

Please sign in to comment.
Something went wrong with that request. Please try again.