GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — develop (#145)
by
unknown
09:16 queued 03:08
created
myth/Controllers/CLIController.php 1 patch
Indentation   +138 added lines, -138 removed lines patch added patch discarded remove patch
@@ -41,144 +41,144 @@
 block discarded – undo
41 41
  */
42 42
 class CLIController extends \CI_Controller {
43 43
 
44
-    /**
45
-     * Holds short descriptions for the public functions in this class.
46
-     * Each 'key' in the array should match the function name.
47
-     *
48
-     * @var array
49
-     */
50
-    protected $descriptions = [];
51
-
52
-    /**
53
-     * Holds long descriptions for the public functions in this class.
54
-     * Each 'key' in the array should match the function name.
55
-     * @var array
56
-     */
57
-    protected $long_descriptions = [];
58
-
59
-    //--------------------------------------------------------------------
60
-
61
-    /**
62
-     * Ensures that we are running on the CLI, and collects basic settings
63
-     * like collecting our command line arguments into a pretty array.
64
-     */
65
-    public function __construct()
66
-    {
67
-        parent::__construct();
68
-
69
-        // Restrict usage to the command line.
70
-        if (! is_cli() )
71
-        {
72
-            show_error( lang('cli_required') );
73
-        }
74
-
75
-        // Make sure the CLI library is loaded and ready.
44
+	/**
45
+	 * Holds short descriptions for the public functions in this class.
46
+	 * Each 'key' in the array should match the function name.
47
+	 *
48
+	 * @var array
49
+	 */
50
+	protected $descriptions = [];
51
+
52
+	/**
53
+	 * Holds long descriptions for the public functions in this class.
54
+	 * Each 'key' in the array should match the function name.
55
+	 * @var array
56
+	 */
57
+	protected $long_descriptions = [];
58
+
59
+	//--------------------------------------------------------------------
60
+
61
+	/**
62
+	 * Ensures that we are running on the CLI, and collects basic settings
63
+	 * like collecting our command line arguments into a pretty array.
64
+	 */
65
+	public function __construct()
66
+	{
67
+		parent::__construct();
68
+
69
+		// Restrict usage to the command line.
70
+		if (! is_cli() )
71
+		{
72
+			show_error( lang('cli_required') );
73
+		}
74
+
75
+		// Make sure the CLI library is loaded and ready.
76 76
 //        CLI::_init();
77
-    }
78
-
79
-    //--------------------------------------------------------------------
80
-
81
-    /**
82
-     * A default index method that all CLI Controllers can share. Will
83
-     * list all of the methods and their short descriptions, if available.
84
-     */
85
-    public function index()
86
-    {
87
-        CLI::new_line();
88
-        CLI::write( lang('cli.available_commands') );
89
-
90
-        $this->sayDescriptions($this->descriptions);
91
-
92
-        CLI::new_line();
93
-    }
94
-
95
-    //--------------------------------------------------------------------
96
-
97
-
98
-    /**
99
-     * Grabs the short description of a command, if it exists.
100
-     *
101
-     * @param null $method
102
-     */
103
-    public function describeMethod($method=null)
104
-    {
105
-        if (empty($this->descriptions[$method]))
106
-        {
107
-            return CLI::error( lang('cli.bad_description') );
108
-        }
109
-
110
-        CLI::write("\t{$this->descriptions[$method]}", 'yellow');
111
-    }
112
-
113
-    //--------------------------------------------------------------------
114
-
115
-    public function longDescribeMethod($method=null)
116
-    {
117
-        if (empty($this->long_descriptions[$method]))
118
-        {
119
-            return CLI::error( lang('cli.no_help') );
120
-        }
121
-
122
-        CLI::write("\t{$this->long_descriptions[$method]}", 'yellow');
123
-    }
124
-
125
-    //--------------------------------------------------------------------
126
-
127
-    //--------------------------------------------------------------------
128
-    // Private Methods
129
-    //--------------------------------------------------------------------
130
-
131
-    protected function sayDescriptions($descriptions)
132
-    {
133
-        $names      = array_keys($descriptions);
134
-        $syntaxes   = array_column($descriptions, 0);
135
-        $descs      = array_column($descriptions, 1);
136
-
137
-        // Pad each item to the same length
138
-        $names      = $this->padArray($names);
139
-        $syntaxes   = $this->padArray($syntaxes);
140
-
141
-        for ($i=0; $i < count($names); $i++)
142
-        {
143
-            $out = CLI::color($names[$i], 'yellow');
144
-
145
-            // The rest of the items stay default color.
146
-            if (isset($syntaxes[$i]))
147
-            {
148
-                $out .= $syntaxes[$i];
149
-            }
150
-
151
-            if (isset($descs[$i]))
152
-            {
153
-                $out .= CLI::wrap($descs[$i], 125, strlen($names[$i]) + strlen($syntaxes[$i]));
154
-            }
155
-
156
-            CLI::write($out);
157
-        }
158
-    }
159
-
160
-    //--------------------------------------------------------------------
161
-
162
-    /**
163
-     * Returns a new array where all of the string elements have
164
-     * been padding with trailling spaces to be the same length.
165
-     *
166
-     * @param array $array
167
-     * @param int $extra // How many extra spaces to add at the end
168
-     * @return array
169
-     */
170
-    protected function padArray($array, $extra=2)
171
-    {
172
-        $max = max(array_map('strlen', $array)) + $extra;
173
-
174
-        foreach ($array as &$item)
175
-        {
176
-            $item = str_pad($item, $max);
177
-        }
178
-
179
-        return $array;
180
-    }
181
-
182
-    //--------------------------------------------------------------------
77
+	}
78
+
79
+	//--------------------------------------------------------------------
80
+
81
+	/**
82
+	 * A default index method that all CLI Controllers can share. Will
83
+	 * list all of the methods and their short descriptions, if available.
84
+	 */
85
+	public function index()
86
+	{
87
+		CLI::new_line();
88
+		CLI::write( lang('cli.available_commands') );
89
+
90
+		$this->sayDescriptions($this->descriptions);
91
+
92
+		CLI::new_line();
93
+	}
94
+
95
+	//--------------------------------------------------------------------
96
+
97
+
98
+	/**
99
+	 * Grabs the short description of a command, if it exists.
100
+	 *
101
+	 * @param null $method
102
+	 */
103
+	public function describeMethod($method=null)
104
+	{
105
+		if (empty($this->descriptions[$method]))
106
+		{
107
+			return CLI::error( lang('cli.bad_description') );
108
+		}
109
+
110
+		CLI::write("\t{$this->descriptions[$method]}", 'yellow');
111
+	}
112
+
113
+	//--------------------------------------------------------------------
114
+
115
+	public function longDescribeMethod($method=null)
116
+	{
117
+		if (empty($this->long_descriptions[$method]))
118
+		{
119
+			return CLI::error( lang('cli.no_help') );
120
+		}
121
+
122
+		CLI::write("\t{$this->long_descriptions[$method]}", 'yellow');
123
+	}
124
+
125
+	//--------------------------------------------------------------------
126
+
127
+	//--------------------------------------------------------------------
128
+	// Private Methods
129
+	//--------------------------------------------------------------------
130
+
131
+	protected function sayDescriptions($descriptions)
132
+	{
133
+		$names      = array_keys($descriptions);
134
+		$syntaxes   = array_column($descriptions, 0);
135
+		$descs      = array_column($descriptions, 1);
136
+
137
+		// Pad each item to the same length
138
+		$names      = $this->padArray($names);
139
+		$syntaxes   = $this->padArray($syntaxes);
140
+
141
+		for ($i=0; $i < count($names); $i++)
142
+		{
143
+			$out = CLI::color($names[$i], 'yellow');
144
+
145
+			// The rest of the items stay default color.
146
+			if (isset($syntaxes[$i]))
147
+			{
148
+				$out .= $syntaxes[$i];
149
+			}
150
+
151
+			if (isset($descs[$i]))
152
+			{
153
+				$out .= CLI::wrap($descs[$i], 125, strlen($names[$i]) + strlen($syntaxes[$i]));
154
+			}
155
+
156
+			CLI::write($out);
157
+		}
158
+	}
159
+
160
+	//--------------------------------------------------------------------
161
+
162
+	/**
163
+	 * Returns a new array where all of the string elements have
164
+	 * been padding with trailling spaces to be the same length.
165
+	 *
166
+	 * @param array $array
167
+	 * @param int $extra // How many extra spaces to add at the end
168
+	 * @return array
169
+	 */
170
+	protected function padArray($array, $extra=2)
171
+	{
172
+		$max = max(array_map('strlen', $array)) + $extra;
173
+
174
+		foreach ($array as &$item)
175
+		{
176
+			$item = str_pad($item, $max);
177
+		}
178
+
179
+		return $array;
180
+	}
181
+
182
+	//--------------------------------------------------------------------
183 183
 
184 184
 }
Please login to merge, or discard this patch.
myth/Controllers/ThemedController.php 1 patch
Indentation   +353 added lines, -353 removed lines patch added patch discarded remove patch
@@ -41,358 +41,358 @@
 block discarded – undo
41 41
  */
42 42
 class ThemedController extends BaseController
43 43
 {
44
-    /**
45
-     * Stores data variables to be sent to the view.
46
-     * @var array
47
-     */
48
-    protected $vars = array();
49
-
50
-    /**
51
-     * Stores current status message.
52
-     * @var
53
-     */
54
-    protected $message;
55
-
56
-    /**
57
-     * The UIKit to make available to the template views.
58
-     * @var string
59
-     */
60
-    protected $uikit = '';
61
-
62
-    /**
63
-     * An instance of an active Themer to use.
64
-     * @var null
65
-     */
66
-    protected $themer = null;
67
-
68
-    /**
69
-     * Allows per-controller override of theme.
70
-     * @var null
71
-     */
72
-    protected $theme = null;
73
-
74
-    /**
75
-     * Per-controller override of the current layout file.
76
-     * @var null
77
-     */
78
-    protected $layout = null;
79
-
80
-    /**
81
-     * Stores an array of javascript files.
82
-     * @var array
83
-     */
84
-    protected $external_scripts = array();
85
-
86
-    /**
87
-     * Stores an array of CSS stylesheets.
88
-     * @var array
89
-     */
90
-    protected $stylesheets = array();
91
-
92
-    /**
93
-     * A MenuCollection instance
94
-     * @var
95
-     */
96
-    protected $meta;
97
-
98
-    /**
99
-     * Whether set() should escape the output...
100
-     * @var bool
101
-     */
102
-    protected $auto_escape = null;
103
-
104
-    /**
105
-     * An instance of ZendFrameworks Escaper
106
-     * @var null
107
-     */
108
-    protected $escaper = null;
109
-
110
-    //--------------------------------------------------------------------
111
-
112
-    /**
113
-     * Constructor takes care of getting the template engine up and running
114
-     * and bound to our DI object, as well as any other preliminary needs,
115
-     * like detecting the variant to use, etc.
116
-     */
117
-    public function __construct()
118
-    {
119
-        parent::__construct();
120
-
121
-        // Setup our Template Engine
122
-        $themer = config_item('active_themer');
123
-
124
-        if (empty($themer)) {
125
-            throw new \RuntimeException( lang('no_themer') );
126
-        }
127
-
128
-        $this->themer = new $themer( get_instance() );
129
-
130
-        // Register our paths with the themer
131
-        $paths = config_item('theme.paths');
132
-
133
-        foreach ($paths as $key => $path) {
134
-            $this->themer->addThemePath($key, $path);
135
-        }
136
-
137
-        // Set our default theme.
138
-        $this->themer->setDefaultTheme( config_item('theme.default_theme') );
139
-
140
-        // Register our variants with the engine.
141
-        $variants = config_item('theme.variants');
142
-
143
-        foreach ($variants as $key => $value) {
144
-            $this->themer->addVariant($key, $value);
145
-        }
146
-
147
-        $this->detectVariant();
148
-
149
-        // Ensure that our UIKit is loaded up if we're using one.
150
-        $uikit = config_item('theme.uikit');
151
-
152
-        if ($uikit)
153
-        {
154
-            $this->uikit = new $uikit();
155
-        }
156
-
157
-        // Load up our meta collection
158
-        $this->meta = new MetaCollection( get_instance() );
159
-
160
-        // Should we autoescape vars?
161
-        if (is_null($this->auto_escape))
162
-        {
163
-            $this->auto_escape = config_item( 'theme.auto_escape' );
164
-        }
165
-    }
166
-
167
-    //--------------------------------------------------------------------
168
-
169
-    /**
170
-     * Provides a common interface with the other rendering methods to
171
-     * set the output of the method. Uses the current instance of $this->template.
172
-     * Ensures that any data we've stored through $this->setVar() are present
173
-     * and includes the status messages into the data.
174
-     *
175
-     * @param array $data
176
-     * @param int   $cache_time
177
-     */
178
-    public function render($data = array(), $cache_time=0)
179
-    {
180
-	    if ($cache_time > 0)
181
-	    {
182
-		    $this->output->cache( (int)$cache_time );
183
-	    }
184
-
185
-        // Determine the correct theme to use
186
-        $theme = ! empty($this->theme) ? $this->theme : config_item('theme.default_theme');
187
-        $this->themer->setTheme($theme);
188
-
189
-        // Determine the correct layout to use
190
-        $layout = !empty($this->layout) ? $this->layout : null;
191
-        $this->themer->setLayout($layout);
192
-
193
-        // Merge any saved vars into the data
194
-        // But first, escape the data if needed
195
-        if ($this->auto_escape)
196
-        {
197
-            $data = esc($data, 'html');
198
-        }
199
-        $data = array_merge($data, $this->vars);
200
-
201
-        // Make sure the MetaCollection is available in the view.
202
-        $data['html_meta'] = $this->meta;
203
-
204
-        // Include our UIKit so views can use it
205
-        if (! empty($this->uikit)) {
206
-            $data['uikit'] = $this->uikit;
207
-        }
208
-
209
-        // Build our notices from the theme's view file.
210
-        $data['notice'] = $this->themer->display($this->themer->theme() . ':notice', ["notice" => $this->message()]);
211
-
212
-        // Make sure any scripts/stylesheets are available to the view
213
-        $data['external_scripts'] = $this->external_scripts;
214
-        $data['stylesheets'] = $this->stylesheets;
215
-
216
-        $this->themer->set($data);
217
-
218
-        $this->output->set_content_type('html')
219
-                     ->set_output($this->themer->render());
220
-    }
221
-
222
-    //--------------------------------------------------------------------
223
-
224
-    /**
225
-     * Sets a data variable to be sent to the view during the render() method.
226
-     * Will auto-escape data on the way in, unless specifically told not to.
227
-     *
228
-     * Uses ZendFramework's Escaper to handle the data escaping,
229
-     * based on context. Valid contexts are:
230
-     *      - html
231
-     *      - htmlAttr
232
-     *      - js
233
-     *      - css
234
-     *      - url
235
-     *
236
-     * @param string $name
237
-     * @param mixed $value
238
-     * @param string $context
239
-     * @param bool $do_escape
240
-     */
241
-    public function setVar($name, $value = null, $context='html', $do_escape=null)
242
-    {
243
-        $escape = $do_escape == true ? true : $this->auto_escape;
244
-
245
-        if (is_null($this->escaper))
246
-        {
247
-            $this->escaper = new Escaper(config_item('charset'));
248
-        }
249
-
250
-        if (is_array($name))
251
-        {
252
-            foreach ($name as $k => $v)
253
-            {
254
-                $this->vars[$k] = $escape ? esc($v, $context, $this->escaper) : $v;
255
-            }
256
-        }
257
-        else
258
-        {
259
-            $this->vars[$name] = $escape ? esc($value, $context, $this->escaper) : $value;
260
-        }
261
-    }
262
-
263
-    //--------------------------------------------------------------------
264
-
265
-    //--------------------------------------------------------------------
266
-    // Status Messages
267
-    //--------------------------------------------------------------------
268
-
269
-    /**
270
-     * Sets a status message (for displaying small success/error messages).
271
-     * This is used in place of the session->flashdata functions since you
272
-     * don't always want to have to refresh the page to show the message.
273
-     *
274
-     * @param string $message The message to save.
275
-     * @param string $type The string to be included as the CSS class of the containing div.
276
-     */
277
-    public function setMessage($message = '', $type = 'info')
278
-    {
279
-        if (! empty($message)) {
280
-            if (isset($this->session)) {
281
-                $this->session->set_flashdata('message', $type . '::' . $message);
282
-            }
283
-
284
-            $this->message = array(
285
-                'type' => $type,
286
-                'message' => $message
287
-            );
288
-        }
289
-    }
290
-
291
-    //--------------------------------------------------------------------
292
-
293
-    /**
294
-     * Retrieves the status message to display (if any).
295
-     *
296
-     * @param  string $message [description]
297
-     * @param  string $type [description]
298
-     * @return array
299
-     */
300
-    public function message($message = '', $type = 'info')
301
-    {
302
-        $return = array(
303
-            'message' => $message,
304
-            'type' => $type
305
-        );
306
-
307
-        // Does session data exist?
308
-        if (empty($message) && class_exists('CI_Session')) {
309
-            $message = $this->session->flashdata('message');
310
-
311
-            if (! empty($message)) {
312
-                // Split out our message parts
313
-                $temp_message = explode('::', $message);
314
-                $return['type'] = $temp_message[0];
315
-                $return['message'] = $temp_message[1];
316
-
317
-                unset($temp_message);
318
-            }
319
-        }
320
-
321
-        // If message is empty, we need to check our own storage.
322
-        if (empty($message)) {
323
-            if (empty($this->message['message'])) {
324
-                return '';
325
-            }
326
-
327
-            $return = $this->message;
328
-        }
329
-
330
-        // Clear our session data so we don't get extra messages on rare occasions.
331
-        if (class_exists('CI_Session')) {
332
-            $this->session->set_flashdata('message', '');
333
-        }
334
-
335
-        return $return;
336
-    }
337
-
338
-    //--------------------------------------------------------------------
339
-
340
-    //--------------------------------------------------------------------
341
-    // Utility Methods
342
-    //--------------------------------------------------------------------
343
-
344
-    /**
345
-     * Detects whether the item is being displayed on a desktop, phone,
346
-     * or tablet device.
347
-     */
348
-    protected function detectVariant()
349
-    {
350
-        // Variant Detection and setup
351
-        if (config_item('autodetect_variant') === true) {
352
-            $detect = new \Mobile_Detect();
353
-
354
-            if ($detect->isMobile()) {
355
-                $this->template->setVariant('phone');
356
-            } else if ($detect->isTablet()) {
357
-                $this->template->setVariant('tablet');
358
-            }
359
-        }
360
-    }
361
-
362
-    //--------------------------------------------------------------------
363
-
364
-    //--------------------------------------------------------------------
365
-    // 'Asset' functions
366
-    //--------------------------------------------------------------------
367
-
368
-    /**
369
-     * Adds an external javascript file to the 'external_scripts' array.
370
-     *
371
-     * @param [type] $filename [description]
372
-     */
373
-    public function addScript($filename)
374
-    {
375
-        if (strpos($filename, 'http') === FALSE) {
376
-            $filename = base_url() . 'assets/js/' . $filename;
377
-        }
378
-
379
-        $this->external_scripts[] = $filename;
380
-    }
381
-
382
-    //--------------------------------------------------------------------
383
-
384
-    /**
385
-     * Adds an external stylesheet file to the 'stylesheets' array.
386
-     */
387
-    public function addStyle($filename)
388
-    {
389
-        if (strpos($filename, 'http') === FALSE) {
390
-            $filename = base_url() . 'assets/css/' . $filename;
391
-        }
392
-
393
-        $this->stylesheets[] = $filename;
394
-    }
395
-
396
-    //--------------------------------------------------------------------
44
+	/**
45
+	 * Stores data variables to be sent to the view.
46
+	 * @var array
47
+	 */
48
+	protected $vars = array();
49
+
50
+	/**
51
+	 * Stores current status message.
52
+	 * @var
53
+	 */
54
+	protected $message;
55
+
56
+	/**
57
+	 * The UIKit to make available to the template views.
58
+	 * @var string
59
+	 */
60
+	protected $uikit = '';
61
+
62
+	/**
63
+	 * An instance of an active Themer to use.
64
+	 * @var null
65
+	 */
66
+	protected $themer = null;
67
+
68
+	/**
69
+	 * Allows per-controller override of theme.
70
+	 * @var null
71
+	 */
72
+	protected $theme = null;
73
+
74
+	/**
75
+	 * Per-controller override of the current layout file.
76
+	 * @var null
77
+	 */
78
+	protected $layout = null;
79
+
80
+	/**
81
+	 * Stores an array of javascript files.
82
+	 * @var array
83
+	 */
84
+	protected $external_scripts = array();
85
+
86
+	/**
87
+	 * Stores an array of CSS stylesheets.
88
+	 * @var array
89
+	 */
90
+	protected $stylesheets = array();
91
+
92
+	/**
93
+	 * A MenuCollection instance
94
+	 * @var
95
+	 */
96
+	protected $meta;
97
+
98
+	/**
99
+	 * Whether set() should escape the output...
100
+	 * @var bool
101
+	 */
102
+	protected $auto_escape = null;
103
+
104
+	/**
105
+	 * An instance of ZendFrameworks Escaper
106
+	 * @var null
107
+	 */
108
+	protected $escaper = null;
109
+
110
+	//--------------------------------------------------------------------
111
+
112
+	/**
113
+	 * Constructor takes care of getting the template engine up and running
114
+	 * and bound to our DI object, as well as any other preliminary needs,
115
+	 * like detecting the variant to use, etc.
116
+	 */
117
+	public function __construct()
118
+	{
119
+		parent::__construct();
120
+
121
+		// Setup our Template Engine
122
+		$themer = config_item('active_themer');
123
+
124
+		if (empty($themer)) {
125
+			throw new \RuntimeException( lang('no_themer') );
126
+		}
127
+
128
+		$this->themer = new $themer( get_instance() );
129
+
130
+		// Register our paths with the themer
131
+		$paths = config_item('theme.paths');
132
+
133
+		foreach ($paths as $key => $path) {
134
+			$this->themer->addThemePath($key, $path);
135
+		}
136
+
137
+		// Set our default theme.
138
+		$this->themer->setDefaultTheme( config_item('theme.default_theme') );
139
+
140
+		// Register our variants with the engine.
141
+		$variants = config_item('theme.variants');
142
+
143
+		foreach ($variants as $key => $value) {
144
+			$this->themer->addVariant($key, $value);
145
+		}
146
+
147
+		$this->detectVariant();
148
+
149
+		// Ensure that our UIKit is loaded up if we're using one.
150
+		$uikit = config_item('theme.uikit');
151
+
152
+		if ($uikit)
153
+		{
154
+			$this->uikit = new $uikit();
155
+		}
156
+
157
+		// Load up our meta collection
158
+		$this->meta = new MetaCollection( get_instance() );
159
+
160
+		// Should we autoescape vars?
161
+		if (is_null($this->auto_escape))
162
+		{
163
+			$this->auto_escape = config_item( 'theme.auto_escape' );
164
+		}
165
+	}
166
+
167
+	//--------------------------------------------------------------------
168
+
169
+	/**
170
+	 * Provides a common interface with the other rendering methods to
171
+	 * set the output of the method. Uses the current instance of $this->template.
172
+	 * Ensures that any data we've stored through $this->setVar() are present
173
+	 * and includes the status messages into the data.
174
+	 *
175
+	 * @param array $data
176
+	 * @param int   $cache_time
177
+	 */
178
+	public function render($data = array(), $cache_time=0)
179
+	{
180
+		if ($cache_time > 0)
181
+		{
182
+			$this->output->cache( (int)$cache_time );
183
+		}
184
+
185
+		// Determine the correct theme to use
186
+		$theme = ! empty($this->theme) ? $this->theme : config_item('theme.default_theme');
187
+		$this->themer->setTheme($theme);
188
+
189
+		// Determine the correct layout to use
190
+		$layout = !empty($this->layout) ? $this->layout : null;
191
+		$this->themer->setLayout($layout);
192
+
193
+		// Merge any saved vars into the data
194
+		// But first, escape the data if needed
195
+		if ($this->auto_escape)
196
+		{
197
+			$data = esc($data, 'html');
198
+		}
199
+		$data = array_merge($data, $this->vars);
200
+
201
+		// Make sure the MetaCollection is available in the view.
202
+		$data['html_meta'] = $this->meta;
203
+
204
+		// Include our UIKit so views can use it
205
+		if (! empty($this->uikit)) {
206
+			$data['uikit'] = $this->uikit;
207
+		}
208
+
209
+		// Build our notices from the theme's view file.
210
+		$data['notice'] = $this->themer->display($this->themer->theme() . ':notice', ["notice" => $this->message()]);
211
+
212
+		// Make sure any scripts/stylesheets are available to the view
213
+		$data['external_scripts'] = $this->external_scripts;
214
+		$data['stylesheets'] = $this->stylesheets;
215
+
216
+		$this->themer->set($data);
217
+
218
+		$this->output->set_content_type('html')
219
+					 ->set_output($this->themer->render());
220
+	}
221
+
222
+	//--------------------------------------------------------------------
223
+
224
+	/**
225
+	 * Sets a data variable to be sent to the view during the render() method.
226
+	 * Will auto-escape data on the way in, unless specifically told not to.
227
+	 *
228
+	 * Uses ZendFramework's Escaper to handle the data escaping,
229
+	 * based on context. Valid contexts are:
230
+	 *      - html
231
+	 *      - htmlAttr
232
+	 *      - js
233
+	 *      - css
234
+	 *      - url
235
+	 *
236
+	 * @param string $name
237
+	 * @param mixed $value
238
+	 * @param string $context
239
+	 * @param bool $do_escape
240
+	 */
241
+	public function setVar($name, $value = null, $context='html', $do_escape=null)
242
+	{
243
+		$escape = $do_escape == true ? true : $this->auto_escape;
244
+
245
+		if (is_null($this->escaper))
246
+		{
247
+			$this->escaper = new Escaper(config_item('charset'));
248
+		}
249
+
250
+		if (is_array($name))
251
+		{
252
+			foreach ($name as $k => $v)
253
+			{
254
+				$this->vars[$k] = $escape ? esc($v, $context, $this->escaper) : $v;
255
+			}
256
+		}
257
+		else
258
+		{
259
+			$this->vars[$name] = $escape ? esc($value, $context, $this->escaper) : $value;
260
+		}
261
+	}
262
+
263
+	//--------------------------------------------------------------------
264
+
265
+	//--------------------------------------------------------------------
266
+	// Status Messages
267
+	//--------------------------------------------------------------------
268
+
269
+	/**
270
+	 * Sets a status message (for displaying small success/error messages).
271
+	 * This is used in place of the session->flashdata functions since you
272
+	 * don't always want to have to refresh the page to show the message.
273
+	 *
274
+	 * @param string $message The message to save.
275
+	 * @param string $type The string to be included as the CSS class of the containing div.
276
+	 */
277
+	public function setMessage($message = '', $type = 'info')
278
+	{
279
+		if (! empty($message)) {
280
+			if (isset($this->session)) {
281
+				$this->session->set_flashdata('message', $type . '::' . $message);
282
+			}
283
+
284
+			$this->message = array(
285
+				'type' => $type,
286
+				'message' => $message
287
+			);
288
+		}
289
+	}
290
+
291
+	//--------------------------------------------------------------------
292
+
293
+	/**
294
+	 * Retrieves the status message to display (if any).
295
+	 *
296
+	 * @param  string $message [description]
297
+	 * @param  string $type [description]
298
+	 * @return array
299
+	 */
300
+	public function message($message = '', $type = 'info')
301
+	{
302
+		$return = array(
303
+			'message' => $message,
304
+			'type' => $type
305
+		);
306
+
307
+		// Does session data exist?
308
+		if (empty($message) && class_exists('CI_Session')) {
309
+			$message = $this->session->flashdata('message');
310
+
311
+			if (! empty($message)) {
312
+				// Split out our message parts
313
+				$temp_message = explode('::', $message);
314
+				$return['type'] = $temp_message[0];
315
+				$return['message'] = $temp_message[1];
316
+
317
+				unset($temp_message);
318
+			}
319
+		}
320
+
321
+		// If message is empty, we need to check our own storage.
322
+		if (empty($message)) {
323
+			if (empty($this->message['message'])) {
324
+				return '';
325
+			}
326
+
327
+			$return = $this->message;
328
+		}
329
+
330
+		// Clear our session data so we don't get extra messages on rare occasions.
331
+		if (class_exists('CI_Session')) {
332
+			$this->session->set_flashdata('message', '');
333
+		}
334
+
335
+		return $return;
336
+	}
337
+
338
+	//--------------------------------------------------------------------
339
+
340
+	//--------------------------------------------------------------------
341
+	// Utility Methods
342
+	//--------------------------------------------------------------------
343
+
344
+	/**
345
+	 * Detects whether the item is being displayed on a desktop, phone,
346
+	 * or tablet device.
347
+	 */
348
+	protected function detectVariant()
349
+	{
350
+		// Variant Detection and setup
351
+		if (config_item('autodetect_variant') === true) {
352
+			$detect = new \Mobile_Detect();
353
+
354
+			if ($detect->isMobile()) {
355
+				$this->template->setVariant('phone');
356
+			} else if ($detect->isTablet()) {
357
+				$this->template->setVariant('tablet');
358
+			}
359
+		}
360
+	}
361
+
362
+	//--------------------------------------------------------------------
363
+
364
+	//--------------------------------------------------------------------
365
+	// 'Asset' functions
366
+	//--------------------------------------------------------------------
367
+
368
+	/**
369
+	 * Adds an external javascript file to the 'external_scripts' array.
370
+	 *
371
+	 * @param [type] $filename [description]
372
+	 */
373
+	public function addScript($filename)
374
+	{
375
+		if (strpos($filename, 'http') === FALSE) {
376
+			$filename = base_url() . 'assets/js/' . $filename;
377
+		}
378
+
379
+		$this->external_scripts[] = $filename;
380
+	}
381
+
382
+	//--------------------------------------------------------------------
383
+
384
+	/**
385
+	 * Adds an external stylesheet file to the 'stylesheets' array.
386
+	 */
387
+	public function addStyle($filename)
388
+	{
389
+		if (strpos($filename, 'http') === FALSE) {
390
+			$filename = base_url() . 'assets/css/' . $filename;
391
+		}
392
+
393
+		$this->stylesheets[] = $filename;
394
+	}
395
+
396
+	//--------------------------------------------------------------------
397 397
 }
398 398
 
Please login to merge, or discard this patch.
myth/Cron/CronManager.php 1 patch
Indentation   +252 added lines, -252 removed lines patch added patch discarded remove patch
@@ -34,257 +34,257 @@
 block discarded – undo
34 34
 
35 35
 class CronManager {
36 36
 
37
-    protected static $tasks = [];
38
-
39
-    //--------------------------------------------------------------------
40
-
41
-    /**
42
-     * Schedules a new task in the system.
43
-     *
44
-     * @param $alias
45
-     * @param $time_string
46
-     * @param callable|string $task
47
-     */
48
-    public static function schedule($alias, $time_string, $task)
49
-    {
50
-        // Valid Alias?
51
-        if (! is_string($alias) || empty($alias))
52
-        {
53
-            throw new \RuntimeException( lang('cron.bad_alias') );
54
-        }
55
-
56
-        // Valid TimeString?
57
-        if (! is_string($time_string) || empty($time_string))
58
-        {
59
-            throw new \RuntimeException( lang('cron.bad_timestring') );
60
-        }
61
-
62
-        // Valid Task?
63
-        if (! is_callable($task) && ! is_string($task))
64
-        {
65
-            throw new \RuntimeException( lang('cron.bad_task') . $alias);
66
-        }
67
-
68
-        static::$tasks[$alias] = new CronTask($time_string, $task);
69
-    }
70
-
71
-    //--------------------------------------------------------------------
72
-
73
-    /**
74
-     * Removes a single task from the system.
75
-     *
76
-     * @param $alias
77
-     */
78
-    public static function remove($alias)
79
-    {
80
-        if (empty(static::$tasks[$alias]))
81
-        {
82
-            return null;
83
-        }
84
-
85
-        unset(static::$tasks[$alias]);
86
-
87
-        return true;
88
-    }
89
-
90
-    //--------------------------------------------------------------------
91
-
92
-
93
-    /**
94
-     * Provides an array of all tasks in the system. The format will be
95
-     * like:
96
-     *      [
97
-     *          'alias' => [
98
-     *              'next_run'  => '123456789',
99
-     *              'prev_run'  => '123456789',
100
-     *              'task'  => mixed
101
-     *          ],
102
-     *          ...
103
-     *      ]
104
-     */
105
-    public static function listAll($current_time='now')
106
-    {
107
-        if (! count(static::$tasks))
108
-        {
109
-            return null;
110
-        }
111
-
112
-        $output = array();
113
-
114
-        foreach (static::$tasks as $alias => $task)
115
-        {
116
-            $output[$alias] = [
117
-                'next_run'  => $task->nextRunDate($current_time),
118
-                'prev_run'  => $task->previousRunDate($current_time),
119
-                'task'      => $task->task()
120
-            ];
121
-        }
122
-
123
-        return $output;
124
-    }
125
-
126
-    //--------------------------------------------------------------------
127
-
128
-    /**
129
-     * Gets the task object assigned to an alias.
130
-     *
131
-     * @param $alias
132
-     * @return null|CronTask object
133
-     */
134
-    public static function task($alias)
135
-    {
136
-        if (empty(static::$tasks[$alias]) )
137
-        {
138
-            return null;
139
-        }
140
-
141
-        return static::$tasks[$alias];
142
-    }
143
-
144
-    //--------------------------------------------------------------------
145
-
146
-    /**
147
-     * Returns all tasks currently in the system.
148
-     *
149
-     * @return array
150
-     */
151
-    public static function tasks()
152
-    {
153
-        return count(static::$tasks) ? static::$tasks : null;
154
-    }
155
-
156
-    //--------------------------------------------------------------------
157
-
158
-    /**
159
-     * Runs all tasks scheduled to run right now.
160
-     *
161
-     * Can be limited to a single task by passing it's alias in the first param.
162
-     *
163
-     * Returns the output of all task runs as a single string. Perfect for
164
-     * emailing to users on completion
165
-     *
166
-     * @param null $alias
167
-     * @param bool $force_run
168
-     * @param string $current_time
169
-     * @return string
170
-     */
171
-    public static function run($alias=null, $force_run=false, $current_time='now')
172
-    {
173
-        $tasks = static::$tasks;
174
-
175
-        if (! empty($alias) && isset($tasks[$alias]))
176
-        {
177
-            $tasks = [$alias => $tasks[$alias] ];
178
-        }
179
-
180
-        $output = '';
181
-
182
-        $count = 0; // How many tasks have ran?
183
-
184
-        foreach ($tasks as $alias => $task)
185
-        {
186
-            if ($task->isDue($current_time) || $force_run === true)
187
-            {
188
-                $output .= sprintf( lang('cron.running_task'), $alias);
189
-
190
-                try {
191
-                    $result = self::runTask($alias);
192
-
193
-                    if (is_bool($result))
194
-                    {
195
-                        $output .= $result === true ? lang('done') ."\n" : lang('failed') ."\n";
196
-                    }
197
-                    else if (is_string($result))
198
-                    {
199
-                        $output .= sprintf( lang('cron.done_with_msg'), $result);
200
-                    }
201
-                }
202
-                catch (\Exception $e)
203
-                {
204
-                    $output .= "[Exception] ". $e->getMessage() ."\n";
205
-                }
206
-
207
-                $count++;
208
-            }
209
-            else
210
-            {
211
-                $output .= sprintf( lang('cron.not_scheduled_until'), $alias, $task->nextRunDate() );
212
-            }
213
-        }
214
-
215
-        if (! $count)
216
-        {
217
-            $output .= lang('cron.nothing_scheduled');
218
-        }
219
-
220
-        return $output;
221
-    }
222
-
223
-    //--------------------------------------------------------------------
224
-
225
-    /**
226
-     * Clears all tasks from the system
227
-     */
228
-    public static function reset()
229
-    {
230
-        static::$tasks = [];
231
-    }
232
-
233
-    //--------------------------------------------------------------------
234
-
235
-    //--------------------------------------------------------------------
236
-    // Protected Methods
237
-    //--------------------------------------------------------------------
238
-
239
-    /**
240
-     * Runs a single Task.
241
-     *
242
-     * NOTE: Tasks MUST return a true/false value only.
243
-     *
244
-     * @param $alias
245
-     * @return bool
246
-     */
247
-    protected static function runTask($alias)
248
-    {
249
-        $task = static::$tasks[$alias]->task();
250
-
251
-        $success = false;
252
-
253
-        // If it's a standard callable item,
254
-        // then run it.
255
-        if (is_callable($task))
256
-        {
257
-            $success = call_user_func($task);
258
-        }
259
-
260
-        // Otherwise, if it's a string it should be
261
-        // a library:method string so try to run it.
262
-        else if (is_string($task) && ! empty($task) && strpos($task, ':') !== false)
263
-        {
264
-            list($class, $method) = explode(':', $task);
265
-
266
-            // Let PHP try to autoload it through any available autoloaders
267
-            // (including Composer and user's custom autoloaders). If we
268
-            // don't find it, then assume it's a CI library that we can reach.
269
-            if (class_exists($class)) {
270
-                $class = new $class();
271
-            } else {
272
-                get_instance()->load->library($class);
273
-                $class =& get_instance()->$class;
274
-            }
275
-
276
-            if (! method_exists($class, $method)) {
277
-                log_message('error', "[CRON] Method not found: {$class}::{$method}");
278
-                return $success;
279
-            }
280
-
281
-            // Call the class with our parameters
282
-            $success = $class->{$method}();
283
-        }
284
-
285
-        return $success;
286
-    }
287
-
288
-    //--------------------------------------------------------------------
37
+	protected static $tasks = [];
38
+
39
+	//--------------------------------------------------------------------
40
+
41
+	/**
42
+	 * Schedules a new task in the system.
43
+	 *
44
+	 * @param $alias
45
+	 * @param $time_string
46
+	 * @param callable|string $task
47
+	 */
48
+	public static function schedule($alias, $time_string, $task)
49
+	{
50
+		// Valid Alias?
51
+		if (! is_string($alias) || empty($alias))
52
+		{
53
+			throw new \RuntimeException( lang('cron.bad_alias') );
54
+		}
55
+
56
+		// Valid TimeString?
57
+		if (! is_string($time_string) || empty($time_string))
58
+		{
59
+			throw new \RuntimeException( lang('cron.bad_timestring') );
60
+		}
61
+
62
+		// Valid Task?
63
+		if (! is_callable($task) && ! is_string($task))
64
+		{
65
+			throw new \RuntimeException( lang('cron.bad_task') . $alias);
66
+		}
67
+
68
+		static::$tasks[$alias] = new CronTask($time_string, $task);
69
+	}
70
+
71
+	//--------------------------------------------------------------------
72
+
73
+	/**
74
+	 * Removes a single task from the system.
75
+	 *
76
+	 * @param $alias
77
+	 */
78
+	public static function remove($alias)
79
+	{
80
+		if (empty(static::$tasks[$alias]))
81
+		{
82
+			return null;
83
+		}
84
+
85
+		unset(static::$tasks[$alias]);
86
+
87
+		return true;
88
+	}
89
+
90
+	//--------------------------------------------------------------------
91
+
92
+
93
+	/**
94
+	 * Provides an array of all tasks in the system. The format will be
95
+	 * like:
96
+	 *      [
97
+	 *          'alias' => [
98
+	 *              'next_run'  => '123456789',
99
+	 *              'prev_run'  => '123456789',
100
+	 *              'task'  => mixed
101
+	 *          ],
102
+	 *          ...
103
+	 *      ]
104
+	 */
105
+	public static function listAll($current_time='now')
106
+	{
107
+		if (! count(static::$tasks))
108
+		{
109
+			return null;
110
+		}
111
+
112
+		$output = array();
113
+
114
+		foreach (static::$tasks as $alias => $task)
115
+		{
116
+			$output[$alias] = [
117
+				'next_run'  => $task->nextRunDate($current_time),
118
+				'prev_run'  => $task->previousRunDate($current_time),
119
+				'task'      => $task->task()
120
+			];
121
+		}
122
+
123
+		return $output;
124
+	}
125
+
126
+	//--------------------------------------------------------------------
127
+
128
+	/**
129
+	 * Gets the task object assigned to an alias.
130
+	 *
131
+	 * @param $alias
132
+	 * @return null|CronTask object
133
+	 */
134
+	public static function task($alias)
135
+	{
136
+		if (empty(static::$tasks[$alias]) )
137
+		{
138
+			return null;
139
+		}
140
+
141
+		return static::$tasks[$alias];
142
+	}
143
+
144
+	//--------------------------------------------------------------------
145
+
146
+	/**
147
+	 * Returns all tasks currently in the system.
148
+	 *
149
+	 * @return array
150
+	 */
151
+	public static function tasks()
152
+	{
153
+		return count(static::$tasks) ? static::$tasks : null;
154
+	}
155
+
156
+	//--------------------------------------------------------------------
157
+
158
+	/**
159
+	 * Runs all tasks scheduled to run right now.
160
+	 *
161
+	 * Can be limited to a single task by passing it's alias in the first param.
162
+	 *
163
+	 * Returns the output of all task runs as a single string. Perfect for
164
+	 * emailing to users on completion
165
+	 *
166
+	 * @param null $alias
167
+	 * @param bool $force_run
168
+	 * @param string $current_time
169
+	 * @return string
170
+	 */
171
+	public static function run($alias=null, $force_run=false, $current_time='now')
172
+	{
173
+		$tasks = static::$tasks;
174
+
175
+		if (! empty($alias) && isset($tasks[$alias]))
176
+		{
177
+			$tasks = [$alias => $tasks[$alias] ];
178
+		}
179
+
180
+		$output = '';
181
+
182
+		$count = 0; // How many tasks have ran?
183
+
184
+		foreach ($tasks as $alias => $task)
185
+		{
186
+			if ($task->isDue($current_time) || $force_run === true)
187
+			{
188
+				$output .= sprintf( lang('cron.running_task'), $alias);
189
+
190
+				try {
191
+					$result = self::runTask($alias);
192
+
193
+					if (is_bool($result))
194
+					{
195
+						$output .= $result === true ? lang('done') ."\n" : lang('failed') ."\n";
196
+					}
197
+					else if (is_string($result))
198
+					{
199
+						$output .= sprintf( lang('cron.done_with_msg'), $result);
200
+					}
201
+				}
202
+				catch (\Exception $e)
203
+				{
204
+					$output .= "[Exception] ". $e->getMessage() ."\n";
205
+				}
206
+
207
+				$count++;
208
+			}
209
+			else
210
+			{
211
+				$output .= sprintf( lang('cron.not_scheduled_until'), $alias, $task->nextRunDate() );
212
+			}
213
+		}
214
+
215
+		if (! $count)
216
+		{
217
+			$output .= lang('cron.nothing_scheduled');
218
+		}
219
+
220
+		return $output;
221
+	}
222
+
223
+	//--------------------------------------------------------------------
224
+
225
+	/**
226
+	 * Clears all tasks from the system
227
+	 */
228
+	public static function reset()
229
+	{
230
+		static::$tasks = [];
231
+	}
232
+
233
+	//--------------------------------------------------------------------
234
+
235
+	//--------------------------------------------------------------------
236
+	// Protected Methods
237
+	//--------------------------------------------------------------------
238
+
239
+	/**
240
+	 * Runs a single Task.
241
+	 *
242
+	 * NOTE: Tasks MUST return a true/false value only.
243
+	 *
244
+	 * @param $alias
245
+	 * @return bool
246
+	 */
247
+	protected static function runTask($alias)
248
+	{
249
+		$task = static::$tasks[$alias]->task();
250
+
251
+		$success = false;
252
+
253
+		// If it's a standard callable item,
254
+		// then run it.
255
+		if (is_callable($task))
256
+		{
257
+			$success = call_user_func($task);
258
+		}
259
+
260
+		// Otherwise, if it's a string it should be
261
+		// a library:method string so try to run it.
262
+		else if (is_string($task) && ! empty($task) && strpos($task, ':') !== false)
263
+		{
264
+			list($class, $method) = explode(':', $task);
265
+
266
+			// Let PHP try to autoload it through any available autoloaders
267
+			// (including Composer and user's custom autoloaders). If we
268
+			// don't find it, then assume it's a CI library that we can reach.
269
+			if (class_exists($class)) {
270
+				$class = new $class();
271
+			} else {
272
+				get_instance()->load->library($class);
273
+				$class =& get_instance()->$class;
274
+			}
275
+
276
+			if (! method_exists($class, $method)) {
277
+				log_message('error', "[CRON] Method not found: {$class}::{$method}");
278
+				return $success;
279
+			}
280
+
281
+			// Call the class with our parameters
282
+			$success = $class->{$method}();
283
+		}
284
+
285
+		return $success;
286
+	}
287
+
288
+	//--------------------------------------------------------------------
289 289
 
290 290
 }
Please login to merge, or discard this patch.
myth/Cron/CronTask.php 1 patch
Indentation   +291 added lines, -291 removed lines patch added patch discarded remove patch
@@ -45,301 +45,301 @@
 block discarded – undo
45 45
  */
46 46
 class CronTask {
47 47
 
48
-    /**
49
-     * The original scheduled string.
50
-     * Any valid relative time string.
51
-     * http://php.net/manual/en/datetime.formats.relative.php
52
-     *
53
-     * @var
54
-     */
55
-    protected $schedule;
56
-
57
-    /**
58
-     * Stores the callable or library name:method to run.
59
-     *
60
-     * @var
61
-     */
62
-    protected $task;
63
-
64
-    //--------------------------------------------------------------------
65
-
66
-    /**
67
-     * Stores our scheduled string and actual task.
68
-     *
69
-     * @param $schedule
70
-     * @param callable $task
71
-     */
72
-    public function __construct($schedule, $task)
73
-    {
74
-        $this->schedule = $schedule;
75
-
76
-        // If $task is not callable, it should be a library:method
77
-        // string that we can parse. But it must have the colon in the string.
78
-        if (! is_callable($task) && strpos($task, ':') === false)
79
-        {
80
-            throw new \RuntimeException( lang('cron.invalid_task') );
81
-        }
82
-
83
-        $this->task = $task;
84
-    }
85
-
86
-    //--------------------------------------------------------------------
87
-
88
-    /**
89
-     * Calculates the next date this task is supposed to run.
90
-     *
91
-     * @param int|'now' $current_time
92
-     *
93
-     * @return timestamp|null
94
-     */
95
-    public function nextRunDate($current_time='now')
96
-    {
97
-        $current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
98
-
99
-        $scheduleType = $this->determineScheduleType($this->schedule);
100
-
101
-        switch ($scheduleType)
102
-        {
103
-            case 'time':
104
-                return $this->findDateInterval($this->schedule, 'next', $current_time);
105
-                break;
106
-            case 'ordinal':
107
-                return strtotime($this->schedule, $current_time);
108
-                break;
109
-            case 'increment':
110
-                return strtotime($this->schedule, $current_time);
111
-                break;
112
-        }
113
-
114
-        return null;
115
-    }
48
+	/**
49
+	 * The original scheduled string.
50
+	 * Any valid relative time string.
51
+	 * http://php.net/manual/en/datetime.formats.relative.php
52
+	 *
53
+	 * @var
54
+	 */
55
+	protected $schedule;
56
+
57
+	/**
58
+	 * Stores the callable or library name:method to run.
59
+	 *
60
+	 * @var
61
+	 */
62
+	protected $task;
63
+
64
+	//--------------------------------------------------------------------
65
+
66
+	/**
67
+	 * Stores our scheduled string and actual task.
68
+	 *
69
+	 * @param $schedule
70
+	 * @param callable $task
71
+	 */
72
+	public function __construct($schedule, $task)
73
+	{
74
+		$this->schedule = $schedule;
75
+
76
+		// If $task is not callable, it should be a library:method
77
+		// string that we can parse. But it must have the colon in the string.
78
+		if (! is_callable($task) && strpos($task, ':') === false)
79
+		{
80
+			throw new \RuntimeException( lang('cron.invalid_task') );
81
+		}
82
+
83
+		$this->task = $task;
84
+	}
85
+
86
+	//--------------------------------------------------------------------
87
+
88
+	/**
89
+	 * Calculates the next date this task is supposed to run.
90
+	 *
91
+	 * @param int|'now' $current_time
92
+	 *
93
+	 * @return timestamp|null
94
+	 */
95
+	public function nextRunDate($current_time='now')
96
+	{
97
+		$current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
98
+
99
+		$scheduleType = $this->determineScheduleType($this->schedule);
100
+
101
+		switch ($scheduleType)
102
+		{
103
+			case 'time':
104
+				return $this->findDateInterval($this->schedule, 'next', $current_time);
105
+				break;
106
+			case 'ordinal':
107
+				return strtotime($this->schedule, $current_time);
108
+				break;
109
+			case 'increment':
110
+				return strtotime($this->schedule, $current_time);
111
+				break;
112
+		}
113
+
114
+		return null;
115
+	}
116 116
     
117
-    //--------------------------------------------------------------------
118
-
119
-    /**
120
-     * Calculates the last time the task should have ran.
121
-     *
122
-     * @param int|'now' $current_time
123
-     *
124
-     * @return timestamp|null
125
-     */
126
-    public function previousRunDate($current_time='now')
127
-    {
128
-        $current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
129
-
130
-        $scheduleType = $this->determineScheduleType($this->schedule);
131
-
132
-        switch ($scheduleType)
133
-        {
134
-            case 'time':
135
-                return $this->findDateInterval($this->schedule, 'prev', $current_time);
136
-                break;
137
-            case 'ordinal':
138
-                return $this->findPreviousOrdinal($this->schedule, $current_time);
139
-                break;
140
-            case 'increment':
141
-                return strtotime('-1 '. $this->schedule, $current_time);
142
-                break;
143
-        }
144
-
145
-        return null;
146
-    }
147
-
148
-    //--------------------------------------------------------------------
149
-
150
-    /**
151
-     * Determines if the task is due to be run now.
152
-     *
153
-     * @param string $current_time
154
-     * @internal param $ int|'now' $current_time
155
-     *
156
-     * @return bool
157
-     */
158
-    public function isDue($current_time='now')
159
-    {
160
-        $current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
161
-
162
-        // For easier matching, and I can't imagine people needing cronjob
163
-        // accuracy to the seconds, we'll just take the current minute.
164
-        return date('Y-m-d H:i', $current_time) == date('Y-m-d H:i', $this->nextRunDate($current_time) );
165
-    }
117
+	//--------------------------------------------------------------------
118
+
119
+	/**
120
+	 * Calculates the last time the task should have ran.
121
+	 *
122
+	 * @param int|'now' $current_time
123
+	 *
124
+	 * @return timestamp|null
125
+	 */
126
+	public function previousRunDate($current_time='now')
127
+	{
128
+		$current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
129
+
130
+		$scheduleType = $this->determineScheduleType($this->schedule);
131
+
132
+		switch ($scheduleType)
133
+		{
134
+			case 'time':
135
+				return $this->findDateInterval($this->schedule, 'prev', $current_time);
136
+				break;
137
+			case 'ordinal':
138
+				return $this->findPreviousOrdinal($this->schedule, $current_time);
139
+				break;
140
+			case 'increment':
141
+				return strtotime('-1 '. $this->schedule, $current_time);
142
+				break;
143
+		}
144
+
145
+		return null;
146
+	}
147
+
148
+	//--------------------------------------------------------------------
149
+
150
+	/**
151
+	 * Determines if the task is due to be run now.
152
+	 *
153
+	 * @param string $current_time
154
+	 * @internal param $ int|'now' $current_time
155
+	 *
156
+	 * @return bool
157
+	 */
158
+	public function isDue($current_time='now')
159
+	{
160
+		$current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
161
+
162
+		// For easier matching, and I can't imagine people needing cronjob
163
+		// accuracy to the seconds, we'll just take the current minute.
164
+		return date('Y-m-d H:i', $current_time) == date('Y-m-d H:i', $this->nextRunDate($current_time) );
165
+	}
166 166
     
167
-    //--------------------------------------------------------------------
168
-
169
-    /**
170
-     * Formats the timestamp produced by nextRunDate and previousRunDate
171
-     * into any format available to date.
172
-     *
173
-     * @param $format_string
174
-     * @return bool|string
175
-     */
176
-    public function format($format_string)
177
-    {
178
-        return date($format_string, strtotime($this->schedule));
179
-    }
180
-
181
-    //--------------------------------------------------------------------
182
-
183
-    /**
184
-     * Gets the associated task.
185
-     *
186
-     * return callable|string
187
-     */
188
-    public function task()
189
-    {
190
-        return $this->task;
191
-    }
192
-
193
-    //--------------------------------------------------------------------
194
-
195
-    /**
196
-     * Gets the original schedule string.
197
-     */
198
-    public function schedule()
199
-    {
200
-        return $this->schedule;
201
-    }
202
-
203
-    //--------------------------------------------------------------------
204
-
205
-    /**
206
-     * Checks the schedule text and determines how we have to treat
207
-     * the schedule when determining next and previous values.
208
-     *
209
-     * Potential Types are:
210
-     *
211
-     *  - increment         Can simply add a +x/-x to the front to get the value.
212
-     *  - time              Something like "every 5 minutes"
213
-     *  - ordinal           Like "first", "second", etc.
214
-     *
215
-     * @param $schedule
216
-     * @return null|string
217
-     */
218
-    public function determineScheduleType($schedule)
219
-    {
220
-        $incs = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sun', 'mon', 'tue',
221
-                'wed', 'thu', 'fri', 'sat', 'weekday', 'weekdays', 'midnight', 'noon'];
222
-        $bigger_incs = [ 'back of', 'front of', 'first day of', 'last day of'];
223
-        $ordinals = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'];
224
-        $schedule = trim( strtolower($schedule) );
225
-
226
-        $multiple_words = strpos($schedule, ' ');
227
-        $first_word = substr($schedule, 0, $multiple_words ? $multiple_words : strlen($schedule));
228
-
229
-        // Is the first character a number? Then it's a time
230
-        if ( is_numeric( $first_word ) )
231
-        {
232
-            return 'time';
233
-        }
234
-
235
-
236
-        // First, try the shorter increments. We do increments in
237
-        // two passes becuase this should be faster than the loop.
238
-        if (in_array($first_word, $incs))
239
-        {
240
-            return 'increment';
241
-        }
242
-
243
-        // But we have to loop before checking ordinals since
244
-        // ordinals may have same first word as these phrases.
245
-        foreach ($bigger_incs as $test)
246
-        {
247
-            if (strpos($schedule, $test) === 0)
248
-            {
249
-                return 'increment';
250
-            }
251
-        }
252
-
253
-        if (in_array($first_word, $ordinals))
254
-        {
255
-            return 'ordinal';
256
-        }
257
-
258
-        return null;
259
-    }
260
-
261
-    //--------------------------------------------------------------------
262
-
263
-    /**
264
-     * Determines the correct time for 'time' type intervals where
265
-     * the timestamp is expected to happen every 'x period', like
266
-     * 'every 5 minutes', every 3 days, etc.
267
-     *
268
-     * @param $schedule
269
-     * @param $type
270
-     * @return float|int|null
271
-     */
272
-    public function findDateInterval($schedule, $type, $current_time='now')
273
-    {
274
-        $current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
167
+	//--------------------------------------------------------------------
168
+
169
+	/**
170
+	 * Formats the timestamp produced by nextRunDate and previousRunDate
171
+	 * into any format available to date.
172
+	 *
173
+	 * @param $format_string
174
+	 * @return bool|string
175
+	 */
176
+	public function format($format_string)
177
+	{
178
+		return date($format_string, strtotime($this->schedule));
179
+	}
180
+
181
+	//--------------------------------------------------------------------
182
+
183
+	/**
184
+	 * Gets the associated task.
185
+	 *
186
+	 * return callable|string
187
+	 */
188
+	public function task()
189
+	{
190
+		return $this->task;
191
+	}
192
+
193
+	//--------------------------------------------------------------------
194
+
195
+	/**
196
+	 * Gets the original schedule string.
197
+	 */
198
+	public function schedule()
199
+	{
200
+		return $this->schedule;
201
+	}
202
+
203
+	//--------------------------------------------------------------------
204
+
205
+	/**
206
+	 * Checks the schedule text and determines how we have to treat
207
+	 * the schedule when determining next and previous values.
208
+	 *
209
+	 * Potential Types are:
210
+	 *
211
+	 *  - increment         Can simply add a +x/-x to the front to get the value.
212
+	 *  - time              Something like "every 5 minutes"
213
+	 *  - ordinal           Like "first", "second", etc.
214
+	 *
215
+	 * @param $schedule
216
+	 * @return null|string
217
+	 */
218
+	public function determineScheduleType($schedule)
219
+	{
220
+		$incs = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sun', 'mon', 'tue',
221
+				'wed', 'thu', 'fri', 'sat', 'weekday', 'weekdays', 'midnight', 'noon'];
222
+		$bigger_incs = [ 'back of', 'front of', 'first day of', 'last day of'];
223
+		$ordinals = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'];
224
+		$schedule = trim( strtolower($schedule) );
225
+
226
+		$multiple_words = strpos($schedule, ' ');
227
+		$first_word = substr($schedule, 0, $multiple_words ? $multiple_words : strlen($schedule));
228
+
229
+		// Is the first character a number? Then it's a time
230
+		if ( is_numeric( $first_word ) )
231
+		{
232
+			return 'time';
233
+		}
234
+
235
+
236
+		// First, try the shorter increments. We do increments in
237
+		// two passes becuase this should be faster than the loop.
238
+		if (in_array($first_word, $incs))
239
+		{
240
+			return 'increment';
241
+		}
242
+
243
+		// But we have to loop before checking ordinals since
244
+		// ordinals may have same first word as these phrases.
245
+		foreach ($bigger_incs as $test)
246
+		{
247
+			if (strpos($schedule, $test) === 0)
248
+			{
249
+				return 'increment';
250
+			}
251
+		}
252
+
253
+		if (in_array($first_word, $ordinals))
254
+		{
255
+			return 'ordinal';
256
+		}
257
+
258
+		return null;
259
+	}
260
+
261
+	//--------------------------------------------------------------------
262
+
263
+	/**
264
+	 * Determines the correct time for 'time' type intervals where
265
+	 * the timestamp is expected to happen every 'x period', like
266
+	 * 'every 5 minutes', every 3 days, etc.
267
+	 *
268
+	 * @param $schedule
269
+	 * @param $type
270
+	 * @return float|int|null
271
+	 */
272
+	public function findDateInterval($schedule, $type, $current_time='now')
273
+	{
274
+		$current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
275 275
 
276 276
 //        list($int, $period) = explode(' ', $schedule);
277 277
 
278
-        $diff = strtotime($schedule, $current_time) - $current_time;
279
-
280
-        $return = null;
281
-
282
-        switch ($type)
283
-        {
284
-            case 'next':
285
-                $next = floor($current_time / $diff) * $diff;
286
-
287
-                // Does next already match the current time?
288
-                if (date('Y-m-d H:i', $next) == date('Y-m-d H:i', $current_time))
289
-                {
290
-                    $return = $next;
291
-                }
292
-                else {
293
-                    $return = $next + $diff;
294
-                }
295
-                break;
296
-            case 'prev':
297
-                $next = ceil($current_time / $diff) * $diff;
298
-                $return = $next - $diff;
299
-                break;
300
-        }
301
-
302
-        if (is_numeric($return))
303
-        {
304
-            $return = (int)$return;
305
-        }
306
-
307
-        return $return;
308
-    }
309
-
310
-    //--------------------------------------------------------------------
311
-
312
-    /**
313
-     * Determines the timestamp of the previous ordinal-based time, like
314
-     * 'second Monday'.
315
-     *
316
-     * @param $schedule
317
-     * @param string $current_time
318
-     * @return int|null
319
-     */
320
-    public function findPreviousOrdinal($schedule, $current_time='now')
321
-    {
322
-        $current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
323
-
324
-        if (empty($schedule)) return null;
325
-
326
-        // Loop through months in reverse, checking each one to
327
-        // see if the ordinal is in the past. If so - wer'e done.
328
-        foreach ([0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12] as $i)
329
-        {
330
-            $lastmonth = strtotime("last day of {$i} month", $current_time);
331
-
332
-            $test = strtotime($schedule, $lastmonth);
333
-
334
-            if ($test <= $current_time)
335
-            {
336
-                return $test;
337
-            }
338
-        }
339
-
340
-        return null;
341
-    }
342
-
343
-    //--------------------------------------------------------------------
278
+		$diff = strtotime($schedule, $current_time) - $current_time;
279
+
280
+		$return = null;
281
+
282
+		switch ($type)
283
+		{
284
+			case 'next':
285
+				$next = floor($current_time / $diff) * $diff;
286
+
287
+				// Does next already match the current time?
288
+				if (date('Y-m-d H:i', $next) == date('Y-m-d H:i', $current_time))
289
+				{
290
+					$return = $next;
291
+				}
292
+				else {
293
+					$return = $next + $diff;
294
+				}
295
+				break;
296
+			case 'prev':
297
+				$next = ceil($current_time / $diff) * $diff;
298
+				$return = $next - $diff;
299
+				break;
300
+		}
301
+
302
+		if (is_numeric($return))
303
+		{
304
+			$return = (int)$return;
305
+		}
306
+
307
+		return $return;
308
+	}
309
+
310
+	//--------------------------------------------------------------------
311
+
312
+	/**
313
+	 * Determines the timestamp of the previous ordinal-based time, like
314
+	 * 'second Monday'.
315
+	 *
316
+	 * @param $schedule
317
+	 * @param string $current_time
318
+	 * @return int|null
319
+	 */
320
+	public function findPreviousOrdinal($schedule, $current_time='now')
321
+	{
322
+		$current_time = is_numeric($current_time) ? (int)$current_time : strtotime($current_time);
323
+
324
+		if (empty($schedule)) return null;
325
+
326
+		// Loop through months in reverse, checking each one to
327
+		// see if the ordinal is in the past. If so - wer'e done.
328
+		foreach ([0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12] as $i)
329
+		{
330
+			$lastmonth = strtotime("last day of {$i} month", $current_time);
331
+
332
+			$test = strtotime($schedule, $lastmonth);
333
+
334
+			if ($test <= $current_time)
335
+			{
336
+				return $test;
337
+			}
338
+		}
339
+
340
+		return null;
341
+	}
342
+
343
+	//--------------------------------------------------------------------
344 344
 
345 345
 }
Please login to merge, or discard this patch.
myth/Docs/Builder.php 1 patch
Indentation   +718 added lines, -718 removed lines patch added patch discarded remove patch
@@ -60,725 +60,725 @@
 block discarded – undo
60 60
 class Builder implements DocBuilderInterface
61 61
 {
62 62
 
63
-    protected $docs_ext = '.md';
64
-
65
-    protected $ignore_files = ['_404.md'];
66
-
67
-    protected $doc_folders = [];
68
-
69
-    /**
70
-     * Stores the current folder alias,
71
-     * once the file has been found.
72
-     *
73
-     * @var null
74
-     */
75
-    protected $current_folder = null;
76
-
77
-    protected $table_classes = 'table table-hover';
78
-
79
-    protected $apppath = '';
80
-
81
-    protected $formatters = [];
82
-
83
-    protected $page_title = null;
84
-
85
-    //--------------------------------------------------------------------
86
-
87
-    public function __construct($config = array())
88
-    {
89
-        $this->apppath = ! empty($config['apppath']) ? rtrim($config['apppath'], '/') . '/' : '';
90
-    }
91
-
92
-    //--------------------------------------------------------------------
93
-
94
-    public function pageTitle()
95
-    {
96
-        return $this->page_title;
97
-    }
98
-
99
-    //--------------------------------------------------------------------
100
-
101
-
102
-
103
-    /**
104
-     * Does the actual work of reading in and parsing the help file.
105
-     * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
106
-     * it will limit it's search to that single folder. If nothing is passed, it will
107
-     * search through all of the folders in the order they were given to the library,
108
-     * until it finds the first one.
109
-     *
110
-     * @param string $path The 'path' of the file (relative to the docs
111
-     *                                 folder. Usually from the URI)
112
-     * @param string $restrictToFolder (Optional) The folder nickname
113
-     *
114
-     * @return string
115
-     */
116
-    public function readPage($path, $restrictToFolder = null)
117
-    {
118
-        // Clean up our path
119
-        $path = trim($path, '/ ');
120
-
121
-        $content = $this->locateAndReadFile($path, $restrictToFolder);
122
-
123
-        $content = $this->parse($content);
124
-
125
-        return $content;
126
-    }
127
-
128
-    //--------------------------------------------------------------------
129
-
130
-    /**
131
-     * Parses the contents. Currently runs through the Markdown Extended
132
-     * parser to convert to HTML.
133
-     *
134
-     * @param $str
135
-     * @return mixed
136
-     */
137
-    public function parse($str)
138
-    {
139
-        return $this->format($str);
140
-    }
141
-
142
-    //--------------------------------------------------------------------
143
-
144
-    /**
145
-     * Perform a few housekeeping tasks on a page, like rewriting URLs to full
146
-     * URLs, not relative, ensuring they link correctly, etc.
147
-     *
148
-     * @param      $content
149
-     * @param null $site_url
150
-     * @param null $current_url
151
-     * @return string   The post-processed HTML.
152
-     */
153
-    public function postProcess($content, $site_url = null, $current_url = null)
154
-    {
155
-        if (empty($content)) {
156
-            return $content;
157
-        }
158
-
159
-        try {
160
-            $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
161
-        } catch (\Exception $e) {
162
-            // SimpleXML barfed on us, so send back the un-modified content
163
-            return $content;
164
-        }
165
-
166
-        // Prepare some things and cleanup others
167
-        $groups = array_keys($this->doc_folders);
168
-        $site_url = rtrim($site_url, '/') . '/';
169
-        $current_url = rtrim($current_url, '#/');
170
-
171
-        // Try to determine the current_url if one isn't set.
172
-        if (empty($this->current_folder)) {
173
-            $this->current_folder = $this->detectCurrentFolder($current_url, $groups);
174
-        }
175
-
176
-        /*
63
+	protected $docs_ext = '.md';
64
+
65
+	protected $ignore_files = ['_404.md'];
66
+
67
+	protected $doc_folders = [];
68
+
69
+	/**
70
+	 * Stores the current folder alias,
71
+	 * once the file has been found.
72
+	 *
73
+	 * @var null
74
+	 */
75
+	protected $current_folder = null;
76
+
77
+	protected $table_classes = 'table table-hover';
78
+
79
+	protected $apppath = '';
80
+
81
+	protected $formatters = [];
82
+
83
+	protected $page_title = null;
84
+
85
+	//--------------------------------------------------------------------
86
+
87
+	public function __construct($config = array())
88
+	{
89
+		$this->apppath = ! empty($config['apppath']) ? rtrim($config['apppath'], '/') . '/' : '';
90
+	}
91
+
92
+	//--------------------------------------------------------------------
93
+
94
+	public function pageTitle()
95
+	{
96
+		return $this->page_title;
97
+	}
98
+
99
+	//--------------------------------------------------------------------
100
+
101
+
102
+
103
+	/**
104
+	 * Does the actual work of reading in and parsing the help file.
105
+	 * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
106
+	 * it will limit it's search to that single folder. If nothing is passed, it will
107
+	 * search through all of the folders in the order they were given to the library,
108
+	 * until it finds the first one.
109
+	 *
110
+	 * @param string $path The 'path' of the file (relative to the docs
111
+	 *                                 folder. Usually from the URI)
112
+	 * @param string $restrictToFolder (Optional) The folder nickname
113
+	 *
114
+	 * @return string
115
+	 */
116
+	public function readPage($path, $restrictToFolder = null)
117
+	{
118
+		// Clean up our path
119
+		$path = trim($path, '/ ');
120
+
121
+		$content = $this->locateAndReadFile($path, $restrictToFolder);
122
+
123
+		$content = $this->parse($content);
124
+
125
+		return $content;
126
+	}
127
+
128
+	//--------------------------------------------------------------------
129
+
130
+	/**
131
+	 * Parses the contents. Currently runs through the Markdown Extended
132
+	 * parser to convert to HTML.
133
+	 *
134
+	 * @param $str
135
+	 * @return mixed
136
+	 */
137
+	public function parse($str)
138
+	{
139
+		return $this->format($str);
140
+	}
141
+
142
+	//--------------------------------------------------------------------
143
+
144
+	/**
145
+	 * Perform a few housekeeping tasks on a page, like rewriting URLs to full
146
+	 * URLs, not relative, ensuring they link correctly, etc.
147
+	 *
148
+	 * @param      $content
149
+	 * @param null $site_url
150
+	 * @param null $current_url
151
+	 * @return string   The post-processed HTML.
152
+	 */
153
+	public function postProcess($content, $site_url = null, $current_url = null)
154
+	{
155
+		if (empty($content)) {
156
+			return $content;
157
+		}
158
+
159
+		try {
160
+			$xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
161
+		} catch (\Exception $e) {
162
+			// SimpleXML barfed on us, so send back the un-modified content
163
+			return $content;
164
+		}
165
+
166
+		// Prepare some things and cleanup others
167
+		$groups = array_keys($this->doc_folders);
168
+		$site_url = rtrim($site_url, '/') . '/';
169
+		$current_url = rtrim($current_url, '#/');
170
+
171
+		// Try to determine the current_url if one isn't set.
172
+		if (empty($this->current_folder)) {
173
+			$this->current_folder = $this->detectCurrentFolder($current_url, $groups);
174
+		}
175
+
176
+		/*
177 177
          * Rewrite the URLs
178 178
          */
179
-        foreach ($xml->xpath('//a') as $link) {
180
-            $link = $this->reformatAnchor($link, $groups, $current_url, $site_url);
181
-        }
182
-
183
-        $content = $xml->asXML();
184
-        $content = trim(str_replace('<?xml version="1.0" standalone="yes"?>', '', $content));
185
-
186
-        // Clean up and style the tables
187
-        $content = str_replace('<table>', '<table class="' . $this->table_classes . '">', $content);
188
-
189
-        return $content;
190
-    }
191
-    //--------------------------------------------------------------------
192
-
193
-    /**
194
-     * Allows users to define the classes that are attached to
195
-     * generated tables.
196
-     *
197
-     * @param null $classes
198
-     * @return $this
199
-     */
200
-    public function setTableClasses($classes = null)
201
-    {
202
-        $this->table_classes = $classes;
203
-
204
-        return $this;
205
-    }
206
-
207
-    //--------------------------------------------------------------------
208
-
209
-    /**
210
-     * Given the contents to render, will build a list of links for the sidebar
211
-     * out of the headings in the file.
212
-     *
213
-     * Note: Will ONLY use h2 and h3 to build the links from.
214
-     *
215
-     * Note: The $content passed in WILL be modified by adding named anchors
216
-     * that match up with the locations.
217
-     *
218
-     * @param string $content The HTML to analyse for headings.
219
-     * @return string
220
-     */
221
-    public function buildDocumentMap(&$content)
222
-    {
223
-        if (empty($content)) {
224
-            return $content;
225
-        }
226
-
227
-        // If $content already has a wrapping <div> and </div> tags, remove them,
228
-        // since we'll replace them just below.
229
-        if (strpos($content, '<div>') === 0) {
230
-            $content = substr($content, 5);
231
-
232
-            // Trailing div also?
233
-            if (substr($content, -6) == '</div>') {
234
-                $content = substr($content, 0, -6);
235
-            }
236
-        }
237
-
238
-        try {
239
-            $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
240
-        } catch (\Exception $e) {
241
-            // SimpleXML barfed on us, so send back the un-modified content
242
-            return [];
243
-        }
244
-
245
-        $map = [];
246
-        list($map, $content) = $this->extractDocMapAndAddAnchors($content, $xml, $map);
247
-
248
-        return $map;
249
-    }
250
-
251
-    //--------------------------------------------------------------------
252
-
253
-    /**
254
-     * Stores the name of the callback method to run to convert the source
255
-     * files to viewable files. By default, this should be used to register
256
-     * a Mardown Extended formatter with the system, but could be used to
257
-     * extend the
258
-     *
259
-     * @param string $callback
260
-     * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
261
-     * @return $this
262
-     */
263
-    public function registerFormatter($callback = null, $cascade = false)
264
-    {
265
-        if (empty($callback)) return;
266
-
267
-        $this->formatters[] = [
268
-            'callable' => $callback,
269
-            'cascade'  => (bool)$cascade
270
-        ];
271
-
272
-        return $this;
273
-    }
274
-
275
-    //--------------------------------------------------------------------
276
-
277
-    /**
278
-     * Runs the text through the registered formatters.
279
-     *
280
-     * @param $str
281
-     * @return mixed
282
-     */
283
-    public function format($str)
284
-    {
285
-        if (! is_array($this->formatters)) return $str;
286
-
287
-        foreach ($this->formatters as $formatter) {
288
-            $method = $formatter['callable'];
289
-            $cascade = $formatter['cascade'];
290
-
291
-            $str = call_user_func($method, $str);
292
-
293
-            if (! $cascade) return $str;
294
-        }
295
-
296
-        return $str;
297
-    }
298
-
299
-    //--------------------------------------------------------------------
300
-
301
-    //--------------------------------------------------------------------
302
-    // Table of Contents methods
303
-    //--------------------------------------------------------------------
304
-
305
-    /**
306
-     * Retrieves the list of files in a folder and preps the name and filename
307
-     * so it's ready for creating the HTML.
308
-     *
309
-     * @param  String $folder The path to the folder to retrieve.
310
-     *
311
-     * @return Array  An associative array @see parse_ini_file for format
312
-     * details.
313
-     */
314
-    public function buildTOC($folder)
315
-    {
316
-        // If the toc file exists in the folder, use it to build the links.
317
-        if (is_file("{$folder}/_toc.ini")) {
318
-            $toc = parse_ini_file("{$folder}/_toc.ini", true);
319
-            return $this->columnizeTOC($toc);
320
-        }
321
-
322
-        // If the toc file does not exist, build the links by listing the files
323
-        // in the directory (and any sub-directories)
324
-        $map = $this->directory_map($folder);
325
-
326
-        // If directory_map can not open the directory or find any files inside
327
-        // the directory, return an empty array.
328
-        if (empty($map)) {
329
-            return [];
330
-        }
331
-
332
-        // If these docs are located in the /application/docs or /bonfire/docs
333
-        // directory, just use $this->current_group for the root.
334
-        // Module docs need $this->current_group and $type.
335
-        $tocRoot = $this->current_folder;
336
-        if ($this->current_folder != strtolower($folder)) {
337
-            $tocRoot .= '/' . strtolower($folder);
338
-        }
339
-
340
-        $toc = [];
341
-        foreach ($map as $files) {
342
-            // If $files isn't an array, then make it one so that all situations
343
-            // may be dealt with cleanly.
344
-            if (! is_array($files)) {
345
-                $files = [$files];
346
-            }
347
-
348
-            foreach ($files as $file) {
349
-                if (in_array($file, $this->ignore_files)) {
350
-                    continue;
351
-                }
352
-
353
-                // The title for the index is the passed $type. Otherwise,
354
-                // build the title from the file's name.
355
-                if (strpos($file, 'index') === false) {
356
-                    $title = str_replace($this->docs_ext, '', $file);
357
-                    $title = str_replace('_', ' ', $title);
358
-                    $title = ucwords($title);
359
-
360
-                    $toc["{$tocRoot}/{$file}"] = $title;
361
-                } else {
362
-                    $toc[$tocRoot] = $type;
363
-                }
364
-            }
365
-        }
366
-
367
-        $toc = $this->columnizeTOC($toc);
368
-
369
-        return $toc;
370
-    }
371
-
372
-    //--------------------------------------------------------------------
373
-
374
-    /**
375
-     * Sorts the passed TOC array into columns of as close to equal length
376
-     * as we can get it.
377
-     *
378
-     * @param $toc
379
-     * @return array
380
-     */
381
-    protected function columnizeTOC($toc)
382
-    {
383
-        $section_count = count($toc);
384
-
385
-        // First - determine the size of each 'section'.
386
-        $sizes = [];
387
-
388
-        foreach ($toc as $section => $chapters) {
389
-            $sizes[] = count($chapters);
390
-        }
391
-
392
-        $column_avg = (int)round(array_sum($sizes) / $section_count);
393
-
394
-        // Split things into 4 columns of approximately equal size.
395
-        // If we only have 4 columns (or less), then make sure to
396
-        // deal with that also.
397
-        $columns = [];
398
-
399
-        $current_column = 0;
400
-        $current_column_count = 0;
401
-        $keys = array_keys($toc);
402
-
403
-        for ($i = 0; $i <= $section_count; $i++) {
404
-            if (! isset($keys[$i])) {
405
-                continue;
406
-            }
407
-
408
-            $section = array_shift($toc);
409
-
410
-            // Can we stay in this column?
411
-            if ($current_column_count <= $column_avg && $section_count > 4) {
412
-                // Don't forget to account for the heading also.
413
-                $current_column_count += count($section) + 1;
414
-            } else {
415
-                $current_column_count = 0;
416
-                $current_column++;
417
-            }
418
-
419
-            $columns[$current_column][$keys[$i]] = $section;
420
-        }
421
-
422
-        return $columns;
423
-    }
424
-
425
-    //--------------------------------------------------------------------
426
-
427
-    //--------------------------------------------------------------------
428
-    // Folder Methods
429
-    //--------------------------------------------------------------------
430
-
431
-    /**
432
-     * Returns the current docFolders array.
433
-     *
434
-     * @return array
435
-     */
436
-    public function docFolders()
437
-    {
438
-        return $this->doc_folders;
439
-    }
440
-
441
-    //--------------------------------------------------------------------
442
-
443
-    /**
444
-     * Registers a path to be used when searching for documentation files.
445
-     *
446
-     * @param $name     A nickname to reference it by later.
447
-     * @param $path     The server path to the folder.
448
-     * @return $this
449
-     */
450
-    public function addDocFolder($name, $path)
451
-    {
452
-        // Standardize the path
453
-        $path = realpath($path) . '/';
454
-
455
-        // realpath will return FALSE if the path doesn't exist
456
-        // or the script doesn't have access to it.
457
-        if (! $path || $path == '/') {
458
-            return $this;
459
-        }
460
-
461
-        $name = strtolower($name);
462
-
463
-        $this->doc_folders[$name] = $path;
464
-
465
-        return $this;
466
-    }
467
-
468
-    //--------------------------------------------------------------------
469
-
470
-    /**
471
-     * Removes a folder from the folders we scan for documentation files
472
-     * within.
473
-     *
474
-     * @param $name
475
-     * @return $this
476
-     */
477
-    public function removeDocFolder($name)
478
-    {
479
-        $name = strtolower($name);
480
-
481
-        if (isset($this->doc_folders[$name])) {
482
-            unset($this->doc_folders[$name]);
483
-        }
484
-
485
-        return $this;
486
-    }
487
-
488
-    //--------------------------------------------------------------------
489
-
490
-    //--------------------------------------------------------------------
491
-    // Private Methods
492
-    //--------------------------------------------------------------------
493
-
494
-    /**
495
-     * Analyzes the passed in current url string and checks against
496
-     * a list of groups to determine what the current group is.
497
-     *
498
-     * @param $current_url
499
-     * @param $groups
500
-     * @return string
501
-     */
502
-    protected function detectCurrentFolder($current_url, $groups = [])
503
-    {
504
-        if (! is_array($groups)) {
505
-            return null;
506
-        }
507
-
508
-        $segments = explode('/', $current_url);
509
-
510
-        // We start from the back of the array since
511
-        // that's most likely to be close to the end.
512
-        $segments = array_reverse($segments);
513
-
514
-        foreach ($segments as $segment) {
515
-            foreach ($groups as $group) {
516
-                if (strtolower($group) == strtolower($segment)) {
517
-                    return $group;
518
-                }
519
-            }
520
-        }
521
-
522
-        // Nothing found?
523
-        return null;
524
-    }
525
-
526
-    //--------------------------------------------------------------------
527
-
528
-    //--------------------------------------------------------------------
529
-    // Private Methods
530
-    //--------------------------------------------------------------------
531
-
532
-    /**
533
-     * Locates the file on disk and reads the contents into a single string.
534
-     *
535
-     * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
536
-     * it will limit it's search to that single folder. If nothing is passed, it will
537
-     * search through all of the folders in the order they were given to the library,
538
-     * until it finds the first one.
539
-     *
540
-     * @param string $path The 'path' of the file (relative to the docs
541
-     *                                 folder. Usually from the URI)
542
-     * @param string $restrictToFolder (Optional) The nickname of one of the
543
-     *                                 folders to restrict the search to.
544
-     *
545
-     * @throws RuntimeException
546
-     * @return null|string
547
-     */
548
-    private function locateAndReadFile($path, $restrictToFolder = null)
549
-    {
550
-        $folders = $this->doc_folders;
551
-
552
-        if (! is_null($restrictToFolder)) {
553
-            // Make sure the folder exists
554
-            if (! is_null($restrictToFolder) && ! isset($this->doc_folders[$restrictToFolder])) {
555
-                throw new \RuntimeException('You must add the docs folder that you wish to find docs from.');
556
-            }
557
-
558
-            $folders = [$this->doc_folders[$restrictToFolder]];
559
-        }
560
-
561
-        foreach ($folders as $alias => $folder) {
562
-            if (file_exists($folder . $path . $this->docs_ext)) {
563
-                // Store the alias so we know which folder we're in.
564
-                $this->current_folder = $alias;
565
-
566
-                return file_get_contents($folder . $path . $this->docs_ext);
567
-            }
568
-        }
569
-
570
-        return null;
571
-    }
572
-
573
-    //--------------------------------------------------------------------
574
-
575
-    /**
576
-     * Re-formats the passed in link.
577
-     *
578
-     * @param $link
579
-     * @param $current_url
580
-     * @param $site_url
581
-     * @return mixed
582
-     */
583
-    private function reformatAnchor($link, $groups, $current_url, $site_url)
584
-    {
585
-        // Grab the href value.
586
-        $href = $link->attributes()->href;
587
-
588
-        // If the href is null, it's probably a named anchor with no content.
589
-        if (! $href) {
590
-            // Make sure it has an href, else the XML will not close this
591
-            // tag correctly.
592
-            $link['href'] = ' ';
593
-
594
-            return $link;
595
-        }
596
-
597
-        // Remove any trailing # signs
598
-        $href = rtrim($href, '# ');
599
-
600
-        // If the href starts with #, then attach the current_url to it
601
-        if ($href != '' && substr_compare($href, '#', 0, 1) === 0) {
602
-            $link['href'] = $current_url . $href;
603
-
604
-            return $link;
605
-        }
606
-
607
-        // If it's a full external path, go on...
608
-        if ((strpos($href, 'http://') !== false || strpos($href, 'https://') !== false) &&
609
-            strpos($href, $site_url) === false
610
-        ) {
611
-            $link['target'] = "_blank";
612
-            return $link;
613
-        }
614
-
615
-        // If it's a full local path, get rid of it.
616
-        if (strpos($href, $site_url) !== false) {
617
-            $href = str_replace($site_url, '', $href);
618
-        }
619
-
620
-        // Strip out some unnecessary items, just in case they're there.
621
-        if (substr($href, 0, strlen('docs/')) == 'docs/') {
622
-            $href = substr($href, strlen('docs/'));
623
-        }
624
-
625
-        // This includes 'bonfire/' if it was missed during the conversion.
626
-        if (substr($href, 0, strlen('bonfire/')) == 'bonfire/') {
627
-            $href = substr($href, strlen('bonfire/'));
628
-        }
629
-
630
-        // If another 'group' is not already defined at the head of the link
631
-        // then add the current group to it.
632
-        $group_found = false;
633
-
634
-        foreach ($groups as $group) {
635
-            if (strpos($href, $group) === 0) {
636
-                $group_found = true;
637
-            }
638
-        }
639
-
640
-        if (! $group_found) {
641
-            $href = $this->current_folder . '/' . $href;
642
-        }
643
-
644
-        // Convert to full site_url
645
-        if (strpos($href, 'http') !== 0) {
646
-            $href = $site_url . 'docs/' . ltrim($href, '/ ');
647
-        }
648
-
649
-        // Save the corrected href
650
-        $link['href'] = $href;
651
-
652
-        return $link;
653
-    }
654
-
655
-    //--------------------------------------------------------------------
656
-
657
-    /**
658
-     * Creates a Document Map based on <h2> and <h3> tags.
659
-     * Also adds named anchors into the $content so the map
660
-     * can link to the content properly.
661
-     *
662
-     * @param $content
663
-     * @param $xml
664
-     * @param $map
665
-     * @return array
666
-     */
667
-    protected function extractDocMapAndAddAnchors(&$content, $xml, $map)
668
-    {
669
-        // Holds the current h2 we're processing
670
-        $current_obj = [];
671
-
672
-        $currentChild = 0;
673
-
674
-        foreach ($xml->children() as $childType => $line) {
675
-            $currentChild++;
676
-
677
-            // If it's an h1 - take the first and make it
678
-            // our page title.
679
-            if ($childType == 'h1' && empty($this->page_title))
680
-            {
681
-                $this->page_title = (string)$line;
682
-            }
683
-
684
-            // Make sure that our current object is
685
-            // stored and reset.
686
-            if ($childType == 'h1' || $childType == 'h2') {
687
-                if (count($current_obj)) {
688
-                    $map[] = $current_obj;
689
-                    $current_obj = [];
690
-                }
691
-            }
692
-
693
-            if ($childType == 'h2') {
694
-                $name = (string)$line;
695
-                $link = strtolower(str_replace(' ', '_', (string)$line));
696
-
697
-                $current_obj['name'] = $name;
698
-                $current_obj['link'] = '#' . $link;
699
-                $current_obj['items'] = [];
700
-
701
-                // Insert a named anchor into the $content
702
-                $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
703
-
704
-                $search = "<h2>{$name}</h2>";
705
-
706
-                $content = str_replace($search, $anchor . $search, $content);
707
-            } elseif ($childType == 'h3') {
708
-                // Make sure we have some place to store the items.
709
-                if (! isset($current_obj['items'])) {
710
-                    $current_obj['items'] = [];
711
-                }
712
-
713
-                $link = strtolower(str_replace(' ', '_', (string)$line));
714
-                $name = (string)$line;
715
-
716
-                $current_obj['items'][] = [
717
-                    'name' => $name,
718
-                    'link' => '#' . $link
719
-                ];
720
-
721
-                // Insert a named anchor into the $content
722
-                $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
723
-
724
-                $search = "<h3>{$name}</h3>";
725
-
726
-                $content = str_replace($search, $anchor . $search, $content);
727
-            }
728
-
729
-            // Is this the last element? Then close out our current object.
730
-            if (count($xml) == $currentChild) {
731
-                if (count($current_obj)) {
732
-                    $map[] = $current_obj;
733
-                }
734
-            }
735
-        }
736
-        return [$map, $content];
737
-    }
738
-    //--------------------------------------------------------------------
739
-
740
-    /**
741
-     * Create a Directory Map
742
-     *
743
-     * Reads the specified directory and builds an array
744
-     * representation of it. Sub-folders contained with the
745
-     * directory will be mapped as well.
746
-     *
747
-     * @param    string $source_dir Path to source
748
-     * @param    int $directory_depth Depth of directories to traverse
749
-     *                        (0 = fully recursive, 1 = current dir, etc)
750
-     * @param    bool $hidden Whether to show hidden files
751
-     * @return    array
752
-     */
753
-    protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
754
-    {
755
-        if ($fp = @opendir($source_dir)) {
756
-            $filedata = array();
757
-            $new_depth = $directory_depth - 1;
758
-            $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
759
-
760
-            while (FALSE !== ($file = readdir($fp))) {
761
-                // Remove '.', '..', and hidden files [optional]
762
-                if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) {
763
-                    continue;
764
-                }
765
-
766
-                is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR;
767
-
768
-                if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file)) {
769
-                    $filedata[$file] = directory_map($source_dir . $file, $new_depth, $hidden);
770
-                } else {
771
-                    $filedata[] = $file;
772
-                }
773
-            }
774
-
775
-            closedir($fp);
776
-            return $filedata;
777
-        }
778
-
779
-        return FALSE;
780
-    }
781
-
782
-    //--------------------------------------------------------------------
179
+		foreach ($xml->xpath('//a') as $link) {
180
+			$link = $this->reformatAnchor($link, $groups, $current_url, $site_url);
181
+		}
182
+
183
+		$content = $xml->asXML();
184
+		$content = trim(str_replace('<?xml version="1.0" standalone="yes"?>', '', $content));
185
+
186
+		// Clean up and style the tables
187
+		$content = str_replace('<table>', '<table class="' . $this->table_classes . '">', $content);
188
+
189
+		return $content;
190
+	}
191
+	//--------------------------------------------------------------------
192
+
193
+	/**
194
+	 * Allows users to define the classes that are attached to
195
+	 * generated tables.
196
+	 *
197
+	 * @param null $classes
198
+	 * @return $this
199
+	 */
200
+	public function setTableClasses($classes = null)
201
+	{
202
+		$this->table_classes = $classes;
203
+
204
+		return $this;
205
+	}
206
+
207
+	//--------------------------------------------------------------------
208
+
209
+	/**
210
+	 * Given the contents to render, will build a list of links for the sidebar
211
+	 * out of the headings in the file.
212
+	 *
213
+	 * Note: Will ONLY use h2 and h3 to build the links from.
214
+	 *
215
+	 * Note: The $content passed in WILL be modified by adding named anchors
216
+	 * that match up with the locations.
217
+	 *
218
+	 * @param string $content The HTML to analyse for headings.
219
+	 * @return string
220
+	 */
221
+	public function buildDocumentMap(&$content)
222
+	{
223
+		if (empty($content)) {
224
+			return $content;
225
+		}
226
+
227
+		// If $content already has a wrapping <div> and </div> tags, remove them,
228
+		// since we'll replace them just below.
229
+		if (strpos($content, '<div>') === 0) {
230
+			$content = substr($content, 5);
231
+
232
+			// Trailing div also?
233
+			if (substr($content, -6) == '</div>') {
234
+				$content = substr($content, 0, -6);
235
+			}
236
+		}
237
+
238
+		try {
239
+			$xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
240
+		} catch (\Exception $e) {
241
+			// SimpleXML barfed on us, so send back the un-modified content
242
+			return [];
243
+		}
244
+
245
+		$map = [];
246
+		list($map, $content) = $this->extractDocMapAndAddAnchors($content, $xml, $map);
247
+
248
+		return $map;
249
+	}
250
+
251
+	//--------------------------------------------------------------------
252
+
253
+	/**
254
+	 * Stores the name of the callback method to run to convert the source
255
+	 * files to viewable files. By default, this should be used to register
256
+	 * a Mardown Extended formatter with the system, but could be used to
257
+	 * extend the
258
+	 *
259
+	 * @param string $callback
260
+	 * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
261
+	 * @return $this
262
+	 */
263
+	public function registerFormatter($callback = null, $cascade = false)
264
+	{
265
+		if (empty($callback)) return;
266
+
267
+		$this->formatters[] = [
268
+			'callable' => $callback,
269
+			'cascade'  => (bool)$cascade
270
+		];
271
+
272
+		return $this;
273
+	}
274
+
275
+	//--------------------------------------------------------------------
276
+
277
+	/**
278
+	 * Runs the text through the registered formatters.
279
+	 *
280
+	 * @param $str
281
+	 * @return mixed
282
+	 */
283
+	public function format($str)
284
+	{
285
+		if (! is_array($this->formatters)) return $str;
286
+
287
+		foreach ($this->formatters as $formatter) {
288
+			$method = $formatter['callable'];
289
+			$cascade = $formatter['cascade'];
290
+
291
+			$str = call_user_func($method, $str);
292
+
293
+			if (! $cascade) return $str;
294
+		}
295
+
296
+		return $str;
297
+	}
298
+
299
+	//--------------------------------------------------------------------
300
+
301
+	//--------------------------------------------------------------------
302
+	// Table of Contents methods
303
+	//--------------------------------------------------------------------
304
+
305
+	/**
306
+	 * Retrieves the list of files in a folder and preps the name and filename
307
+	 * so it's ready for creating the HTML.
308
+	 *
309
+	 * @param  String $folder The path to the folder to retrieve.
310
+	 *
311
+	 * @return Array  An associative array @see parse_ini_file for format
312
+	 * details.
313
+	 */
314
+	public function buildTOC($folder)
315
+	{
316
+		// If the toc file exists in the folder, use it to build the links.
317
+		if (is_file("{$folder}/_toc.ini")) {
318
+			$toc = parse_ini_file("{$folder}/_toc.ini", true);
319
+			return $this->columnizeTOC($toc);
320
+		}
321
+
322
+		// If the toc file does not exist, build the links by listing the files
323
+		// in the directory (and any sub-directories)
324
+		$map = $this->directory_map($folder);
325
+
326
+		// If directory_map can not open the directory or find any files inside
327
+		// the directory, return an empty array.
328
+		if (empty($map)) {
329
+			return [];
330
+		}
331
+
332
+		// If these docs are located in the /application/docs or /bonfire/docs
333
+		// directory, just use $this->current_group for the root.
334
+		// Module docs need $this->current_group and $type.
335
+		$tocRoot = $this->current_folder;
336
+		if ($this->current_folder != strtolower($folder)) {
337
+			$tocRoot .= '/' . strtolower($folder);
338
+		}
339
+
340
+		$toc = [];
341
+		foreach ($map as $files) {
342
+			// If $files isn't an array, then make it one so that all situations
343
+			// may be dealt with cleanly.
344
+			if (! is_array($files)) {
345
+				$files = [$files];
346
+			}
347
+
348
+			foreach ($files as $file) {
349
+				if (in_array($file, $this->ignore_files)) {
350
+					continue;
351
+				}
352
+
353
+				// The title for the index is the passed $type. Otherwise,
354
+				// build the title from the file's name.
355
+				if (strpos($file, 'index') === false) {
356
+					$title = str_replace($this->docs_ext, '', $file);
357
+					$title = str_replace('_', ' ', $title);
358
+					$title = ucwords($title);
359
+
360
+					$toc["{$tocRoot}/{$file}"] = $title;
361
+				} else {
362
+					$toc[$tocRoot] = $type;
363
+				}
364
+			}
365
+		}
366
+
367
+		$toc = $this->columnizeTOC($toc);
368
+
369
+		return $toc;
370
+	}
371
+
372
+	//--------------------------------------------------------------------
373
+
374
+	/**
375
+	 * Sorts the passed TOC array into columns of as close to equal length
376
+	 * as we can get it.
377
+	 *
378
+	 * @param $toc
379
+	 * @return array
380
+	 */
381
+	protected function columnizeTOC($toc)
382
+	{
383
+		$section_count = count($toc);
384
+
385
+		// First - determine the size of each 'section'.
386
+		$sizes = [];
387
+
388
+		foreach ($toc as $section => $chapters) {
389
+			$sizes[] = count($chapters);
390
+		}
391
+
392
+		$column_avg = (int)round(array_sum($sizes) / $section_count);
393
+
394
+		// Split things into 4 columns of approximately equal size.
395
+		// If we only have 4 columns (or less), then make sure to
396
+		// deal with that also.
397
+		$columns = [];
398
+
399
+		$current_column = 0;
400
+		$current_column_count = 0;
401
+		$keys = array_keys($toc);
402
+
403
+		for ($i = 0; $i <= $section_count; $i++) {
404
+			if (! isset($keys[$i])) {
405
+				continue;
406
+			}
407
+
408
+			$section = array_shift($toc);
409
+
410
+			// Can we stay in this column?
411
+			if ($current_column_count <= $column_avg && $section_count > 4) {
412
+				// Don't forget to account for the heading also.
413
+				$current_column_count += count($section) + 1;
414
+			} else {
415
+				$current_column_count = 0;
416
+				$current_column++;
417
+			}
418
+
419
+			$columns[$current_column][$keys[$i]] = $section;
420
+		}
421
+
422
+		return $columns;
423
+	}
424
+
425
+	//--------------------------------------------------------------------
426
+
427
+	//--------------------------------------------------------------------
428
+	// Folder Methods
429
+	//--------------------------------------------------------------------
430
+
431
+	/**
432
+	 * Returns the current docFolders array.
433
+	 *
434
+	 * @return array
435
+	 */
436
+	public function docFolders()
437
+	{
438
+		return $this->doc_folders;
439
+	}
440
+
441
+	//--------------------------------------------------------------------
442
+
443
+	/**
444
+	 * Registers a path to be used when searching for documentation files.
445
+	 *
446
+	 * @param $name     A nickname to reference it by later.
447
+	 * @param $path     The server path to the folder.
448
+	 * @return $this
449
+	 */
450
+	public function addDocFolder($name, $path)
451
+	{
452
+		// Standardize the path
453
+		$path = realpath($path) . '/';
454
+
455
+		// realpath will return FALSE if the path doesn't exist
456
+		// or the script doesn't have access to it.
457
+		if (! $path || $path == '/') {
458
+			return $this;
459
+		}
460
+
461
+		$name = strtolower($name);
462
+
463
+		$this->doc_folders[$name] = $path;
464
+
465
+		return $this;
466
+	}
467
+
468
+	//--------------------------------------------------------------------
469
+
470
+	/**
471
+	 * Removes a folder from the folders we scan for documentation files
472
+	 * within.
473
+	 *
474
+	 * @param $name
475
+	 * @return $this
476
+	 */
477
+	public function removeDocFolder($name)
478
+	{
479
+		$name = strtolower($name);
480
+
481
+		if (isset($this->doc_folders[$name])) {
482
+			unset($this->doc_folders[$name]);
483
+		}
484
+
485
+		return $this;
486
+	}
487
+
488
+	//--------------------------------------------------------------------
489
+
490
+	//--------------------------------------------------------------------
491
+	// Private Methods
492
+	//--------------------------------------------------------------------
493
+
494
+	/**
495
+	 * Analyzes the passed in current url string and checks against
496
+	 * a list of groups to determine what the current group is.
497
+	 *
498
+	 * @param $current_url
499
+	 * @param $groups
500
+	 * @return string
501
+	 */
502
+	protected function detectCurrentFolder($current_url, $groups = [])
503
+	{
504
+		if (! is_array($groups)) {
505
+			return null;
506
+		}
507
+
508
+		$segments = explode('/', $current_url);
509
+
510
+		// We start from the back of the array since
511
+		// that's most likely to be close to the end.
512
+		$segments = array_reverse($segments);
513
+
514
+		foreach ($segments as $segment) {
515
+			foreach ($groups as $group) {
516
+				if (strtolower($group) == strtolower($segment)) {
517
+					return $group;
518
+				}
519
+			}
520
+		}
521
+
522
+		// Nothing found?
523
+		return null;
524
+	}
525
+
526
+	//--------------------------------------------------------------------
527
+
528
+	//--------------------------------------------------------------------
529
+	// Private Methods
530
+	//--------------------------------------------------------------------
531
+
532
+	/**
533
+	 * Locates the file on disk and reads the contents into a single string.
534
+	 *
535
+	 * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
536
+	 * it will limit it's search to that single folder. If nothing is passed, it will
537
+	 * search through all of the folders in the order they were given to the library,
538
+	 * until it finds the first one.
539
+	 *
540
+	 * @param string $path The 'path' of the file (relative to the docs
541
+	 *                                 folder. Usually from the URI)
542
+	 * @param string $restrictToFolder (Optional) The nickname of one of the
543
+	 *                                 folders to restrict the search to.
544
+	 *
545
+	 * @throws RuntimeException
546
+	 * @return null|string
547
+	 */
548
+	private function locateAndReadFile($path, $restrictToFolder = null)
549
+	{
550
+		$folders = $this->doc_folders;
551
+
552
+		if (! is_null($restrictToFolder)) {
553
+			// Make sure the folder exists
554
+			if (! is_null($restrictToFolder) && ! isset($this->doc_folders[$restrictToFolder])) {
555
+				throw new \RuntimeException('You must add the docs folder that you wish to find docs from.');
556
+			}
557
+
558
+			$folders = [$this->doc_folders[$restrictToFolder]];
559
+		}
560
+
561
+		foreach ($folders as $alias => $folder) {
562
+			if (file_exists($folder . $path . $this->docs_ext)) {
563
+				// Store the alias so we know which folder we're in.
564
+				$this->current_folder = $alias;
565
+
566
+				return file_get_contents($folder . $path . $this->docs_ext);
567
+			}
568
+		}
569
+
570
+		return null;
571
+	}
572
+
573
+	//--------------------------------------------------------------------
574
+
575
+	/**
576
+	 * Re-formats the passed in link.
577
+	 *
578
+	 * @param $link
579
+	 * @param $current_url
580
+	 * @param $site_url
581
+	 * @return mixed
582
+	 */
583
+	private function reformatAnchor($link, $groups, $current_url, $site_url)
584
+	{
585
+		// Grab the href value.
586
+		$href = $link->attributes()->href;
587
+
588
+		// If the href is null, it's probably a named anchor with no content.
589
+		if (! $href) {
590
+			// Make sure it has an href, else the XML will not close this
591
+			// tag correctly.
592
+			$link['href'] = ' ';
593
+
594
+			return $link;
595
+		}
596
+
597
+		// Remove any trailing # signs
598
+		$href = rtrim($href, '# ');
599
+
600
+		// If the href starts with #, then attach the current_url to it
601
+		if ($href != '' && substr_compare($href, '#', 0, 1) === 0) {
602
+			$link['href'] = $current_url . $href;
603
+
604
+			return $link;
605
+		}
606
+
607
+		// If it's a full external path, go on...
608
+		if ((strpos($href, 'http://') !== false || strpos($href, 'https://') !== false) &&
609
+			strpos($href, $site_url) === false
610
+		) {
611
+			$link['target'] = "_blank";
612
+			return $link;
613
+		}
614
+
615
+		// If it's a full local path, get rid of it.
616
+		if (strpos($href, $site_url) !== false) {
617
+			$href = str_replace($site_url, '', $href);
618
+		}
619
+
620
+		// Strip out some unnecessary items, just in case they're there.
621
+		if (substr($href, 0, strlen('docs/')) == 'docs/') {
622
+			$href = substr($href, strlen('docs/'));
623
+		}
624
+
625
+		// This includes 'bonfire/' if it was missed during the conversion.
626
+		if (substr($href, 0, strlen('bonfire/')) == 'bonfire/') {
627
+			$href = substr($href, strlen('bonfire/'));
628
+		}
629
+
630
+		// If another 'group' is not already defined at the head of the link
631
+		// then add the current group to it.
632
+		$group_found = false;
633
+
634
+		foreach ($groups as $group) {
635
+			if (strpos($href, $group) === 0) {
636
+				$group_found = true;
637
+			}
638
+		}
639
+
640
+		if (! $group_found) {
641
+			$href = $this->current_folder . '/' . $href;
642
+		}
643
+
644
+		// Convert to full site_url
645
+		if (strpos($href, 'http') !== 0) {
646
+			$href = $site_url . 'docs/' . ltrim($href, '/ ');
647
+		}
648
+
649
+		// Save the corrected href
650
+		$link['href'] = $href;
651
+
652
+		return $link;
653
+	}
654
+
655
+	//--------------------------------------------------------------------
656
+
657
+	/**
658
+	 * Creates a Document Map based on <h2> and <h3> tags.
659
+	 * Also adds named anchors into the $content so the map
660
+	 * can link to the content properly.
661
+	 *
662
+	 * @param $content
663
+	 * @param $xml
664
+	 * @param $map
665
+	 * @return array
666
+	 */
667
+	protected function extractDocMapAndAddAnchors(&$content, $xml, $map)
668
+	{
669
+		// Holds the current h2 we're processing
670
+		$current_obj = [];
671
+
672
+		$currentChild = 0;
673
+
674
+		foreach ($xml->children() as $childType => $line) {
675
+			$currentChild++;
676
+
677
+			// If it's an h1 - take the first and make it
678
+			// our page title.
679
+			if ($childType == 'h1' && empty($this->page_title))
680
+			{
681
+				$this->page_title = (string)$line;
682
+			}
683
+
684
+			// Make sure that our current object is
685
+			// stored and reset.
686
+			if ($childType == 'h1' || $childType == 'h2') {
687
+				if (count($current_obj)) {
688
+					$map[] = $current_obj;
689
+					$current_obj = [];
690
+				}
691
+			}
692
+
693
+			if ($childType == 'h2') {
694
+				$name = (string)$line;
695
+				$link = strtolower(str_replace(' ', '_', (string)$line));
696
+
697
+				$current_obj['name'] = $name;
698
+				$current_obj['link'] = '#' . $link;
699
+				$current_obj['items'] = [];
700
+
701
+				// Insert a named anchor into the $content
702
+				$anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
703
+
704
+				$search = "<h2>{$name}</h2>";
705
+
706
+				$content = str_replace($search, $anchor . $search, $content);
707
+			} elseif ($childType == 'h3') {
708
+				// Make sure we have some place to store the items.
709
+				if (! isset($current_obj['items'])) {
710
+					$current_obj['items'] = [];
711
+				}
712
+
713
+				$link = strtolower(str_replace(' ', '_', (string)$line));
714
+				$name = (string)$line;
715
+
716
+				$current_obj['items'][] = [
717
+					'name' => $name,
718
+					'link' => '#' . $link
719
+				];
720
+
721
+				// Insert a named anchor into the $content
722
+				$anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
723
+
724
+				$search = "<h3>{$name}</h3>";
725
+
726
+				$content = str_replace($search, $anchor . $search, $content);
727
+			}
728
+
729
+			// Is this the last element? Then close out our current object.
730
+			if (count($xml) == $currentChild) {
731
+				if (count($current_obj)) {
732
+					$map[] = $current_obj;
733
+				}
734
+			}
735
+		}
736
+		return [$map, $content];
737
+	}
738
+	//--------------------------------------------------------------------
739
+
740
+	/**
741
+	 * Create a Directory Map
742
+	 *
743
+	 * Reads the specified directory and builds an array
744
+	 * representation of it. Sub-folders contained with the
745
+	 * directory will be mapped as well.
746
+	 *
747
+	 * @param    string $source_dir Path to source
748
+	 * @param    int $directory_depth Depth of directories to traverse
749
+	 *                        (0 = fully recursive, 1 = current dir, etc)
750
+	 * @param    bool $hidden Whether to show hidden files
751
+	 * @return    array
752
+	 */
753
+	protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
754
+	{
755
+		if ($fp = @opendir($source_dir)) {
756
+			$filedata = array();
757
+			$new_depth = $directory_depth - 1;
758
+			$source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
759
+
760
+			while (FALSE !== ($file = readdir($fp))) {
761
+				// Remove '.', '..', and hidden files [optional]
762
+				if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) {
763
+					continue;
764
+				}
765
+
766
+				is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR;
767
+
768
+				if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file)) {
769
+					$filedata[$file] = directory_map($source_dir . $file, $new_depth, $hidden);
770
+				} else {
771
+					$filedata[] = $file;
772
+				}
773
+			}
774
+
775
+			closedir($fp);
776
+			return $filedata;
777
+		}
778
+
779
+		return FALSE;
780
+	}
781
+
782
+	//--------------------------------------------------------------------
783 783
 
784 784
 }
Please login to merge, or discard this patch.
myth/Docs/DocBuilderInterface.php 1 patch
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -40,117 +40,117 @@
 block discarded – undo
40 40
  */
41 41
 interface DocBuilderInterface
42 42
 {
43
-    /**
44
-     * Does the actual work of reading in and parsing the help file.
45
-     * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
46
-     * it will limit it's search to that single folder. If nothing is passed, it will
47
-     * search through all of the folders in the order they were given to the library,
48
-     * until it finds the first one.
49
-     *
50
-     * @param string $path The 'path' of the file (relative to the docs
51
-     *                                 folder. Usually from the URI)
52
-     * @param string $restrictToFolder (Optional) The folder nickname
53
-     *
54
-     * @return string
55
-     */
56
-    public function readPage($path, $restrictToFolder = null);
43
+	/**
44
+	 * Does the actual work of reading in and parsing the help file.
45
+	 * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
46
+	 * it will limit it's search to that single folder. If nothing is passed, it will
47
+	 * search through all of the folders in the order they were given to the library,
48
+	 * until it finds the first one.
49
+	 *
50
+	 * @param string $path The 'path' of the file (relative to the docs
51
+	 *                                 folder. Usually from the URI)
52
+	 * @param string $restrictToFolder (Optional) The folder nickname
53
+	 *
54
+	 * @return string
55
+	 */
56
+	public function readPage($path, $restrictToFolder = null);
57 57
 
58
-    /**
59
-     * Parses the contents. Currently runs through the Markdown Extended
60
-     * parser to convert to HTML.
61
-     *
62
-     * @param $str
63
-     * @return mixed
64
-     */
65
-    public function parse($str);
58
+	/**
59
+	 * Parses the contents. Currently runs through the Markdown Extended
60
+	 * parser to convert to HTML.
61
+	 *
62
+	 * @param $str
63
+	 * @return mixed
64
+	 */
65
+	public function parse($str);
66 66
 
67
-    /**
68
-     * Perform a few housekeeping tasks on a page, like rewriting URLs to full
69
-     * URLs, not relative, ensuring they link correctly, etc.
70
-     *
71
-     * @param      $content
72
-     * @param null $site_url
73
-     * @param null $current_url
74
-     * @return string   The post-processed HTML.
75
-     */
76
-    public function postProcess($content, $site_url = null, $current_url = null);
67
+	/**
68
+	 * Perform a few housekeeping tasks on a page, like rewriting URLs to full
69
+	 * URLs, not relative, ensuring they link correctly, etc.
70
+	 *
71
+	 * @param      $content
72
+	 * @param null $site_url
73
+	 * @param null $current_url
74
+	 * @return string   The post-processed HTML.
75
+	 */
76
+	public function postProcess($content, $site_url = null, $current_url = null);
77 77
 
78
-    /**
79
-     * Allows users to define the classes that are attached to
80
-     * generated tables.
81
-     *
82
-     * @param null $classes
83
-     * @return $this
84
-     */
85
-    public function setTableClasses($classes = null);
78
+	/**
79
+	 * Allows users to define the classes that are attached to
80
+	 * generated tables.
81
+	 *
82
+	 * @param null $classes
83
+	 * @return $this
84
+	 */
85
+	public function setTableClasses($classes = null);
86 86
 
87
-    /**
88
-     * Given the contents to render, will build a list of links for the sidebar
89
-     * out of the headings in the file.
90
-     *
91
-     * Note: Will ONLY use h2 and h3 to build the links from.
92
-     *
93
-     * Note: The $content passed in WILL be modified by adding named anchors
94
-     * that match up with the locations.
95
-     *
96
-     * @param string $content The HTML to analyse for headings.
97
-     * @return string
98
-     */
99
-    public function buildDocumentMap(&$content);
87
+	/**
88
+	 * Given the contents to render, will build a list of links for the sidebar
89
+	 * out of the headings in the file.
90
+	 *
91
+	 * Note: Will ONLY use h2 and h3 to build the links from.
92
+	 *
93
+	 * Note: The $content passed in WILL be modified by adding named anchors
94
+	 * that match up with the locations.
95
+	 *
96
+	 * @param string $content The HTML to analyse for headings.
97
+	 * @return string
98
+	 */
99
+	public function buildDocumentMap(&$content);
100 100
 
101
-    /**
102
-     * Stores the name of the callback method to run to convert the source
103
-     * files to viewable files. By default, this should be used to register
104
-     * a Mardown Extended formatter with the system, but could be used to
105
-     * extend the
106
-     *
107
-     * @param string $callback_name
108
-     * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
109
-     * @return $this
110
-     */
111
-    public function registerFormatter($callback_name = '', $cascade = false);
101
+	/**
102
+	 * Stores the name of the callback method to run to convert the source
103
+	 * files to viewable files. By default, this should be used to register
104
+	 * a Mardown Extended formatter with the system, but could be used to
105
+	 * extend the
106
+	 *
107
+	 * @param string $callback_name
108
+	 * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
109
+	 * @return $this
110
+	 */
111
+	public function registerFormatter($callback_name = '', $cascade = false);
112 112
 
113
-    /**
114
-     * Runs the text through the registered formatters.
115
-     *
116
-     * @param $str
117
-     * @return mixed
118
-     */
119
-    public function format($str);
113
+	/**
114
+	 * Runs the text through the registered formatters.
115
+	 *
116
+	 * @param $str
117
+	 * @return mixed
118
+	 */
119
+	public function format($str);
120 120
 
121
-    /**
122
-     * Retrieves the list of files in a folder and preps the name and filename
123
-     * so it's ready for creating the HTML.
124
-     *
125
-     * @param  String $folder The path to the folder to retrieve.
126
-     *
127
-     * @return Array  An associative array @see parse_ini_file for format
128
-     * details.
129
-     */
130
-    public function buildTOC($folder);
121
+	/**
122
+	 * Retrieves the list of files in a folder and preps the name and filename
123
+	 * so it's ready for creating the HTML.
124
+	 *
125
+	 * @param  String $folder The path to the folder to retrieve.
126
+	 *
127
+	 * @return Array  An associative array @see parse_ini_file for format
128
+	 * details.
129
+	 */
130
+	public function buildTOC($folder);
131 131
 
132
-    /**
133
-     * Returns the current docFolders array.
134
-     *
135
-     * @return array
136
-     */
137
-    public function docFolders();
132
+	/**
133
+	 * Returns the current docFolders array.
134
+	 *
135
+	 * @return array
136
+	 */
137
+	public function docFolders();
138 138
 
139
-    /**
140
-     * Registers a path to be used when searching for documentation files.
141
-     *
142
-     * @param $name     A nickname to reference it by later.
143
-     * @param $path     The server path to the folder.
144
-     * @return $this
145
-     */
146
-    public function addDocFolder($name, $path);
139
+	/**
140
+	 * Registers a path to be used when searching for documentation files.
141
+	 *
142
+	 * @param $name     A nickname to reference it by later.
143
+	 * @param $path     The server path to the folder.
144
+	 * @return $this
145
+	 */
146
+	public function addDocFolder($name, $path);
147 147
 
148
-    /**
149
-     * Removes a folder from the folders we scan for documentation files
150
-     * within.
151
-     *
152
-     * @param $name
153
-     * @return $this
154
-     */
155
-    public function removeDocFolder($name);
148
+	/**
149
+	 * Removes a folder from the folders we scan for documentation files
150
+	 * within.
151
+	 *
152
+	 * @param $name
153
+	 * @return $this
154
+	 */
155
+	public function removeDocFolder($name);
156 156
 }
Please login to merge, or discard this patch.
myth/Docs/DocSearchInterface.php 1 patch
Indentation   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -1,71 +1,71 @@
 block discarded – undo
1 1
 <?php namespace Myth\Docs;
2 2
 /**
3
- * Sprint
4
- *
5
- * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining a copy
8
- * of this software and associated documentation files (the "Software"), to deal
9
- * in the Software without restriction, including without limitation the rights
10
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the Software is
12
- * furnished to do so, subject to the following conditions:
13
- *
14
- * The above copyright notice and this permission notice shall be included in
15
- * all copies or substantial portions of the Software.
16
- *
17
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- * THE SOFTWARE.
24
- *
25
- * @package     Sprint
26
- * @author      Lonnie Ezell
27
- * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
- * @license     http://opensource.org/licenses/MIT  (MIT)
29
- * @link        http://sprintphp.com
30
- * @since       Version 1.0
31
- */
3
+	 * Sprint
4
+	 *
5
+	 * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
+	 *
7
+	 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+	 * of this software and associated documentation files (the "Software"), to deal
9
+	 * in the Software without restriction, including without limitation the rights
10
+	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+	 * copies of the Software, and to permit persons to whom the Software is
12
+	 * furnished to do so, subject to the following conditions:
13
+	 *
14
+	 * The above copyright notice and this permission notice shall be included in
15
+	 * all copies or substantial portions of the Software.
16
+	 *
17
+	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+	 * THE SOFTWARE.
24
+	 *
25
+	 * @package     Sprint
26
+	 * @author      Lonnie Ezell
27
+	 * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
+	 * @license     http://opensource.org/licenses/MIT  (MIT)
29
+	 * @link        http://sprintphp.com
30
+	 * @since       Version 1.0
31
+	 */
32 32
 
33 33
 interface DocSearchInterface {
34 34
 
35
-    /**
36
-     * The entry point for performing a search of the documentation.
37
-     *
38
-     * @param null  $terms
39
-     * @param array $folders
40
-     *
41
-     * @return array|null
42
-     */
43
-    public function search($terms = null, $folders = []);
35
+	/**
36
+	 * The entry point for performing a search of the documentation.
37
+	 *
38
+	 * @param null  $terms
39
+	 * @param array $folders
40
+	 *
41
+	 * @return array|null
42
+	 */
43
+	public function search($terms = null, $folders = []);
44 44
 
45
-    //--------------------------------------------------------------------
45
+	//--------------------------------------------------------------------
46 46
 
47
-    /**
48
-     * Stores the name of the callback method to run to convert the source
49
-     * files to viewable files. By default, this should be used to register
50
-     * a Mardown Extended formatter with the system, but could be used to
51
-     * extend the
52
-     *
53
-     * @param string $callback_name
54
-     * @param bool   $cascade       // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
55
-     * @return $this
56
-     */
57
-    public function registerFormatter($callback_name='', $cascade=false);
47
+	/**
48
+	 * Stores the name of the callback method to run to convert the source
49
+	 * files to viewable files. By default, this should be used to register
50
+	 * a Mardown Extended formatter with the system, but could be used to
51
+	 * extend the
52
+	 *
53
+	 * @param string $callback_name
54
+	 * @param bool   $cascade       // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
55
+	 * @return $this
56
+	 */
57
+	public function registerFormatter($callback_name='', $cascade=false);
58 58
 
59
-    //--------------------------------------------------------------------
59
+	//--------------------------------------------------------------------
60 60
 
61
-    /**
62
-     * Runs the text through the registered formatters.
63
-     *
64
-     * @param $str
65
-     * @return mixed
66
-     */
67
-    public function format($str);
61
+	/**
62
+	 * Runs the text through the registered formatters.
63
+	 *
64
+	 * @param $str
65
+	 * @return mixed
66
+	 */
67
+	public function format($str);
68 68
 
69
-    //--------------------------------------------------------------------
69
+	//--------------------------------------------------------------------
70 70
 
71 71
 }
Please login to merge, or discard this patch.
myth/Docs/Search.php 1 patch
Indentation   +415 added lines, -415 removed lines patch added patch discarded remove patch
@@ -41,420 +41,420 @@
 block discarded – undo
41 41
 class Search implements DocSearchInterface
42 42
 {
43 43
 
44
-    /**
45
-     * Minimum characters that can be submitted for a search.
46
-     *
47
-     * @var int
48
-     */
49
-    protected $min_chars = 3;
50
-
51
-    /**
52
-     * Maximum characters that can be submitted for a search.
53
-     *
54
-     * @var int
55
-     */
56
-    protected $max_chars = 30;
57
-
58
-    /**
59
-     * Valid file extensions we can search in.
60
-     *
61
-     * @var string
62
-     */
63
-    protected $allowed_file_types = 'html|htm|php|php4|php5|txt|md';
64
-
65
-    /**
66
-     * Which files should we skip over during our search?
67
-     *
68
-     * @var array
69
-     */
70
-    protected $skip_files = ['.', '..', '_404.md', '_toc.ini'];
71
-
72
-    /**
73
-     * How much of each file should we read.
74
-     * Use lower values for faster searches.
75
-     *
76
-     * @var int
77
-     */
78
-    protected $byte_size = 51200;
79
-
80
-    /**
81
-     * Number of words long (approximately)
82
-     * the result excerpt should be.
83
-     *
84
-     * @var int
85
-     */
86
-    protected $excerpt_length = 60;
87
-
88
-    /**
89
-     * The maximum number of results allowed from a single file.
90
-     *
91
-     * @var int
92
-     */
93
-    protected $max_per_file = 1;
94
-
95
-    protected $doc_folders = array();
96
-
97
-    protected $formatters = array();
98
-
99
-    //--------------------------------------------------------------------
100
-
101
-    /**
102
-     * The entry point for performing a search of the documentation.
103
-     *
104
-     * @param null $terms
105
-     * @param array $folders
106
-     *
107
-     * @return array|null
108
-     */
109
-    public function search($terms = null, $folders = [])
110
-    {
111
-        if (empty($terms) || empty($folders)) {
112
-            return null;
113
-        }
114
-
115
-        $results = [];
116
-        $this->doc_folders = $folders;
117
-
118
-        foreach ($folders as $group => $folder) {
119
-            $results = array_merge($results, $this->searchFolder($terms, $folder, $group));
120
-        }
121
-
122
-        return $results;
123
-    }
124
-
125
-    //--------------------------------------------------------------------
126
-
127
-    //--------------------------------------------------------------------
128
-    // Private Methods
129
-    //--------------------------------------------------------------------
130
-
131
-
132
-    /**
133
-     * Searches a single directory worth of files.
134
-     *
135
-     * @param $term
136
-     * @param $folder
137
-     * @param $group_name
138
-     *
139
-     * @return array The results.
140
-     */
141
-    protected function searchFolder($term, $folder, $group_name)
142
-    {
143
-        $results = [];
144
-
145
-        $map = $this->directory_map($folder, 2);
146
-
147
-        $map = $this->flattenMap($map);
148
-
149
-        // Make sure we have something to work with.
150
-        if (! is_array($map) || (is_array($map) && ! count($map))) {
151
-            return [];
152
-        }
153
-
154
-        // Loop over each file and search the contents for our term.
155
-        foreach ($map as $dir => $file) {
156
-            $file_count = 0;
157
-
158
-            if (in_array($file, $this->skip_files)) {
159
-                continue;
160
-            }
161
-
162
-            // Is it a folder?
163
-            if (is_array($file) && count($file)) {
164
-                $results = array_merge($results, $this->searchFolder($term, $folder . '/' . $dir, $group_name));
165
-                continue;
166
-            }
167
-
168
-            // Make sure it's the right file type...
169
-            if (! preg_match("/({$this->allowed_file_types})/i", $file)) {
170
-                continue;
171
-            }
172
-
173
-            $path = is_string($dir) ? $folder . '/' . $dir . '/' . $file : $folder . '/' . $file;
174
-            $term_html = htmlentities($term);
175
-
176
-            // Read in the file text
177
-            $handle = fopen($path, 'r');
178
-            $text = fread($handle, $this->byte_size);
179
-
180
-            // Do we have a match in here somewhere?
181
-            $found = stristr($text, $term) || stristr($text, $term_html);
182
-
183
-            if (! $found) {
184
-                continue;
185
-            }
186
-
187
-            // Escape our terms to safely use in a preg_match
188
-            $excerpt = strip_tags($text);
189
-            $term = preg_quote($term);
190
-            $term = str_replace("/", "\/", "{$term}");
191
-            $term_html = preg_quote($term_html);
192
-            $term_html = str_replace("/", "\/", "{$term_html}");
193
-
194
-            // Add the item to our results with extracts.
195
-            if (preg_match_all(
196
-                "/((\s\S*){0,3})($term|$term_html)((\s?\S*){0,3})/i",
197
-                $excerpt,
198
-                $matches,
199
-                PREG_OFFSET_CAPTURE | PREG_SET_ORDER
200
-            )) {
201
-                foreach ($matches as $match) {
202
-                    if ($file_count >= $this->max_per_file) {
203
-                        continue;
204
-                    }
205
-                    $result_url = '/docs/' . $group_name . '/' . str_replace('.md', '', $file);
206
-
207
-                    foreach ($this->doc_folders as $alias => $folder) {
208
-                        $result_url = str_replace($folder, $alias, $result_url);
209
-                    }
210
-
211
-                    $results[] = [
212
-                        'title' => $this->extractTitle($excerpt, $file),
213
-                        'file' => $folder . '/' . $file,
214
-                        'url' => $result_url,
215
-                        'extract' => $this->buildExtract($excerpt, $term, $match[0][0])
216
-                    ];
217
-
218
-                    $file_count++;
219
-                }
220
-            }
221
-        }
222
-
223
-        return $results;
224
-    }
225
-
226
-    //--------------------------------------------------------------------
227
-
228
-    /**
229
-     * Stores the name of the callback method to run to convert the source
230
-     * files to viewable files. By default, this should be used to register
231
-     * a Mardown Extended formatter with the system, but could be used to
232
-     * extend the
233
-     *
234
-     * @param string $callback_name
235
-     * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
236
-     * @return $this
237
-     */
238
-    public function registerFormatter($callback_name = '', $cascade = false)
239
-    {
240
-        if (empty($callback_name)) return;
241
-
242
-        $this->formatters[] = array($callback_name => $cascade);
243
-
244
-        return $this;
245
-    }
246
-
247
-    //--------------------------------------------------------------------
248
-
249
-    /**
250
-     * Runs the text through the registered formatters.
251
-     *
252
-     * @param $str
253
-     * @return mixed
254
-     */
255
-    public function format($str)
256
-    {
257
-        if (! is_array($this->formatters)) return $str;
258
-
259
-        foreach ($this->formatters as $formatter) {
260
-            $method = key($formatter);
261
-            $cascade = $formatter[$method];
262
-
263
-            $str = call_user_func($method, $str);
264
-
265
-            if (! $cascade) return $str;
266
-        }
267
-
268
-        return $str;
269
-    }
270
-
271
-    //--------------------------------------------------------------------
272
-
273
-
274
-    //--------------------------------------------------------------------
275
-    // Protected Methods
276
-    //--------------------------------------------------------------------
277
-
278
-    /**
279
-     * Converts an array generated by directory_map into a flat array of
280
-     * folders, removing any nested folders and adding them to the path.
281
-     *
282
-     * @param $map
283
-     * @param $prefix   Used to recursively add the folder name...
284
-     * @return mixed
285
-     */
286
-    protected function flattenMap($map, $prefix = '')
287
-    {
288
-        if (! is_array($map) || ! count($map)) {
289
-            return $map;
290
-        }
291
-
292
-        $return = [];
293
-
294
-        foreach ($map as $folder => $files) {
295
-
296
-            // If it's a folder name and an array of files
297
-            // then call this method recursively to flatten it out.
298
-            if (is_array($files)) {
299
-                $return = array_merge($return, $this->flattenMap($files, $prefix . $folder));
300
-                continue;
301
-            }
302
-
303
-            // Else, add our prefix (if any) to the filename...
304
-            $return[] = $prefix . $files;
305
-        }
306
-
307
-        return $return;
308
-    }
309
-
310
-    //--------------------------------------------------------------------
311
-
312
-    /**
313
-     * Handles extracting the text surrounding our match and basic match formatting.
314
-     *
315
-     * @param $excerpt
316
-     * @param $term
317
-     * @param $match_string
318
-     *
319
-     * @return string
320
-     */
321
-    protected function buildExtract($excerpt, $term, $match_string)
322
-    {
323
-        // Find the character positions within the string that our match was found at.
324
-        // That way we'll know from what positions before and after this we want to grab it in.
325
-        $start_offset = stripos($excerpt, $match_string);
326
-
327
-        // Modify the start and end positions based on $this->excerpt_length / 2.
328
-        $buffer = floor($this->excerpt_length / 2);
329
-
330
-        // Adjust our start position
331
-        $start_offset = $start_offset - $buffer;
332
-        if ($start_offset < 0) {
333
-            $start_offset = 0;
334
-        }
335
-
336
-        $extract = substr($excerpt, $start_offset);
337
-
338
-        $extract = strip_tags($this->format($extract));
339
-
340
-        $extract = $this->firstXWords($extract, $this->excerpt_length);
341
-
342
-        // Wrap the search term in a span we can style.
343
-        $extract = str_ireplace($term, '<span class="term-hilight">' . $term . '</span>', $extract);
344
-
345
-        return $extract;
346
-    }
347
-
348
-    //--------------------------------------------------------------------
349
-
350
-    /**
351
-     * Extracts the title from a bit of markdown formatted text. If it doesn't
352
-     * have an h1 or h2, then it uses the filename.
353
-     *
354
-     * @param $excerpt
355
-     * @param $file
356
-     * @return string
357
-     */
358
-    protected function extractTitle($excerpt, $file)
359
-    {
360
-        $title = '';
361
-
362
-        // Easiest to work if this is split into lines.
363
-        $lines = explode("\n", $excerpt);
364
-
365
-        if (is_array($lines) && count($lines)) {
366
-            foreach ($lines as $line) {
367
-                if (strpos($line, '# ') === 0 || strpos($line, '## ') === 0) {
368
-                    $title = trim(str_replace('#', '', $line));
369
-                    break;
370
-                }
371
-            }
372
-        }
373
-
374
-        // If it's empty, we'll use the filename.
375
-        if (empty($title)) {
376
-            $title = str_replace('_', ' ', $file);
377
-            $title = str_replace('.md', ' ', $title);
378
-            $title = ucwords($title);
379
-        }
380
-
381
-        return $title;
382
-    }
383
-    //--------------------------------------------------------------------
384
-
385
-    /**
386
-     * Create a Directory Map
387
-     *
388
-     * Reads the specified directory and builds an array
389
-     * representation of it. Sub-folders contained with the
390
-     * directory will be mapped as well.
391
-     *
392
-     * @param    string $source_dir Path to source
393
-     * @param    int $directory_depth Depth of directories to traverse
394
-     *                        (0 = fully recursive, 1 = current dir, etc)
395
-     * @param    bool $hidden Whether to show hidden files
396
-     * @return    array
397
-     */
398
-    protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
399
-    {
400
-        if ($fp = @opendir($source_dir)) {
401
-            $filedata = array();
402
-            $new_depth = $directory_depth - 1;
403
-            $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
404
-
405
-            while (FALSE !== ($file = readdir($fp))) {
406
-                // Remove '.', '..', and hidden files [optional]
407
-                if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) {
408
-                    continue;
409
-                }
410
-
411
-                is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR;
412
-
413
-                if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file))
414
-                {
415
-                    $filedata[$file] = $this->directory_map($source_dir . $file, $new_depth, $hidden);
416
-                } else
417
-                {
418
-                    // Replace the directory separator here with a forward slash since
419
-                    // Windows uses backward slashes and not all browsers will auto-replace
420
-                    // those slashes in URLs.
421
-                    $filedata[] = str_replace(DIRECTORY_SEPARATOR, '/', $file);
422
-                }
423
-            }
424
-
425
-            closedir($fp);
426
-            return $filedata;
427
-        }
428
-
429
-        return FALSE;
430
-    }
431
-
432
-    //--------------------------------------------------------------------
433
-
434
-    /**
435
-     * Gets the first 'X' words of a string.
436
-     *
437
-     * @param $str
438
-     * @param int $wordCount
439
-     * @return string
440
-     */
441
-    protected function firstXWords($str, $wordCount = 10)
442
-    {
443
-        return implode(
444
-            '',
445
-            array_slice(
446
-                preg_split(
447
-                    '/([\s,\.;\?\!]+)/',
448
-                    $str,
449
-                    $wordCount * 2 + 1,
450
-                    PREG_SPLIT_DELIM_CAPTURE
451
-                ),
452
-                0,
453
-                $wordCount * 2 - 1
454
-            )
455
-        );
456
-    }
457
-
458
-    //--------------------------------------------------------------------
44
+	/**
45
+	 * Minimum characters that can be submitted for a search.
46
+	 *
47
+	 * @var int
48
+	 */
49
+	protected $min_chars = 3;
50
+
51
+	/**
52
+	 * Maximum characters that can be submitted for a search.
53
+	 *
54
+	 * @var int
55
+	 */
56
+	protected $max_chars = 30;
57
+
58
+	/**
59
+	 * Valid file extensions we can search in.
60
+	 *
61
+	 * @var string
62
+	 */
63
+	protected $allowed_file_types = 'html|htm|php|php4|php5|txt|md';
64
+
65
+	/**
66
+	 * Which files should we skip over during our search?
67
+	 *
68
+	 * @var array
69
+	 */
70
+	protected $skip_files = ['.', '..', '_404.md', '_toc.ini'];
71
+
72
+	/**
73
+	 * How much of each file should we read.
74
+	 * Use lower values for faster searches.
75
+	 *
76
+	 * @var int
77
+	 */
78
+	protected $byte_size = 51200;
79
+
80
+	/**
81
+	 * Number of words long (approximately)
82
+	 * the result excerpt should be.
83
+	 *
84
+	 * @var int
85
+	 */
86
+	protected $excerpt_length = 60;
87
+
88
+	/**
89
+	 * The maximum number of results allowed from a single file.
90
+	 *
91
+	 * @var int
92
+	 */
93
+	protected $max_per_file = 1;
94
+
95
+	protected $doc_folders = array();
96
+
97
+	protected $formatters = array();
98
+
99
+	//--------------------------------------------------------------------
100
+
101
+	/**
102
+	 * The entry point for performing a search of the documentation.
103
+	 *
104
+	 * @param null $terms
105
+	 * @param array $folders
106
+	 *
107
+	 * @return array|null
108
+	 */
109
+	public function search($terms = null, $folders = [])
110
+	{
111
+		if (empty($terms) || empty($folders)) {
112
+			return null;
113
+		}
114
+
115
+		$results = [];
116
+		$this->doc_folders = $folders;
117
+
118
+		foreach ($folders as $group => $folder) {
119
+			$results = array_merge($results, $this->searchFolder($terms, $folder, $group));
120
+		}
121
+
122
+		return $results;
123
+	}
124
+
125
+	//--------------------------------------------------------------------
126
+
127
+	//--------------------------------------------------------------------
128
+	// Private Methods
129
+	//--------------------------------------------------------------------
130
+
131
+
132
+	/**
133
+	 * Searches a single directory worth of files.
134
+	 *
135
+	 * @param $term
136
+	 * @param $folder
137
+	 * @param $group_name
138
+	 *
139
+	 * @return array The results.
140
+	 */
141
+	protected function searchFolder($term, $folder, $group_name)
142
+	{
143
+		$results = [];
144
+
145
+		$map = $this->directory_map($folder, 2);
146
+
147
+		$map = $this->flattenMap($map);
148
+
149
+		// Make sure we have something to work with.
150
+		if (! is_array($map) || (is_array($map) && ! count($map))) {
151
+			return [];
152
+		}
153
+
154
+		// Loop over each file and search the contents for our term.
155
+		foreach ($map as $dir => $file) {
156
+			$file_count = 0;
157
+
158
+			if (in_array($file, $this->skip_files)) {
159
+				continue;
160
+			}
161
+
162
+			// Is it a folder?
163
+			if (is_array($file) && count($file)) {
164
+				$results = array_merge($results, $this->searchFolder($term, $folder . '/' . $dir, $group_name));
165
+				continue;
166
+			}
167
+
168
+			// Make sure it's the right file type...
169
+			if (! preg_match("/({$this->allowed_file_types})/i", $file)) {
170
+				continue;
171
+			}
172
+
173
+			$path = is_string($dir) ? $folder . '/' . $dir . '/' . $file : $folder . '/' . $file;
174
+			$term_html = htmlentities($term);
175
+
176
+			// Read in the file text
177
+			$handle = fopen($path, 'r');
178
+			$text = fread($handle, $this->byte_size);
179
+
180
+			// Do we have a match in here somewhere?
181
+			$found = stristr($text, $term) || stristr($text, $term_html);
182
+
183
+			if (! $found) {
184
+				continue;
185
+			}
186
+
187
+			// Escape our terms to safely use in a preg_match
188
+			$excerpt = strip_tags($text);
189
+			$term = preg_quote($term);
190
+			$term = str_replace("/", "\/", "{$term}");
191
+			$term_html = preg_quote($term_html);
192
+			$term_html = str_replace("/", "\/", "{$term_html}");
193
+
194
+			// Add the item to our results with extracts.
195
+			if (preg_match_all(
196
+				"/((\s\S*){0,3})($term|$term_html)((\s?\S*){0,3})/i",
197
+				$excerpt,
198
+				$matches,
199
+				PREG_OFFSET_CAPTURE | PREG_SET_ORDER
200
+			)) {
201
+				foreach ($matches as $match) {
202
+					if ($file_count >= $this->max_per_file) {
203
+						continue;
204
+					}
205
+					$result_url = '/docs/' . $group_name . '/' . str_replace('.md', '', $file);
206
+
207
+					foreach ($this->doc_folders as $alias => $folder) {
208
+						$result_url = str_replace($folder, $alias, $result_url);
209
+					}
210
+
211
+					$results[] = [
212
+						'title' => $this->extractTitle($excerpt, $file),
213
+						'file' => $folder . '/' . $file,
214
+						'url' => $result_url,
215
+						'extract' => $this->buildExtract($excerpt, $term, $match[0][0])
216
+					];
217
+
218
+					$file_count++;
219
+				}
220
+			}
221
+		}
222
+
223
+		return $results;
224
+	}
225
+
226
+	//--------------------------------------------------------------------
227
+
228
+	/**
229
+	 * Stores the name of the callback method to run to convert the source
230
+	 * files to viewable files. By default, this should be used to register
231
+	 * a Mardown Extended formatter with the system, but could be used to
232
+	 * extend the
233
+	 *
234
+	 * @param string $callback_name
235
+	 * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
236
+	 * @return $this
237
+	 */
238
+	public function registerFormatter($callback_name = '', $cascade = false)
239
+	{
240
+		if (empty($callback_name)) return;
241
+
242
+		$this->formatters[] = array($callback_name => $cascade);
243
+
244
+		return $this;
245
+	}
246
+
247
+	//--------------------------------------------------------------------
248
+
249
+	/**
250
+	 * Runs the text through the registered formatters.
251
+	 *
252
+	 * @param $str
253
+	 * @return mixed
254
+	 */
255
+	public function format($str)
256
+	{
257
+		if (! is_array($this->formatters)) return $str;
258
+
259
+		foreach ($this->formatters as $formatter) {
260
+			$method = key($formatter);
261
+			$cascade = $formatter[$method];
262
+
263
+			$str = call_user_func($method, $str);
264
+
265
+			if (! $cascade) return $str;
266
+		}
267
+
268
+		return $str;
269
+	}
270
+
271
+	//--------------------------------------------------------------------
272
+
273
+
274
+	//--------------------------------------------------------------------
275
+	// Protected Methods
276
+	//--------------------------------------------------------------------
277
+
278
+	/**
279
+	 * Converts an array generated by directory_map into a flat array of
280
+	 * folders, removing any nested folders and adding them to the path.
281
+	 *
282
+	 * @param $map
283
+	 * @param $prefix   Used to recursively add the folder name...
284
+	 * @return mixed
285
+	 */
286
+	protected function flattenMap($map, $prefix = '')
287
+	{
288
+		if (! is_array($map) || ! count($map)) {
289
+			return $map;
290
+		}
291
+
292
+		$return = [];
293
+
294
+		foreach ($map as $folder => $files) {
295
+
296
+			// If it's a folder name and an array of files
297
+			// then call this method recursively to flatten it out.
298
+			if (is_array($files)) {
299
+				$return = array_merge($return, $this->flattenMap($files, $prefix . $folder));
300
+				continue;
301
+			}
302
+
303
+			// Else, add our prefix (if any) to the filename...
304
+			$return[] = $prefix . $files;
305
+		}
306
+
307
+		return $return;
308
+	}
309
+
310
+	//--------------------------------------------------------------------
311
+
312
+	/**
313
+	 * Handles extracting the text surrounding our match and basic match formatting.
314
+	 *
315
+	 * @param $excerpt
316
+	 * @param $term
317
+	 * @param $match_string
318
+	 *
319
+	 * @return string
320
+	 */
321
+	protected function buildExtract($excerpt, $term, $match_string)
322
+	{
323
+		// Find the character positions within the string that our match was found at.
324
+		// That way we'll know from what positions before and after this we want to grab it in.
325
+		$start_offset = stripos($excerpt, $match_string);
326
+
327
+		// Modify the start and end positions based on $this->excerpt_length / 2.
328
+		$buffer = floor($this->excerpt_length / 2);
329
+
330
+		// Adjust our start position
331
+		$start_offset = $start_offset - $buffer;
332
+		if ($start_offset < 0) {
333
+			$start_offset = 0;
334
+		}
335
+
336
+		$extract = substr($excerpt, $start_offset);
337
+
338
+		$extract = strip_tags($this->format($extract));
339
+
340
+		$extract = $this->firstXWords($extract, $this->excerpt_length);
341
+
342
+		// Wrap the search term in a span we can style.
343
+		$extract = str_ireplace($term, '<span class="term-hilight">' . $term . '</span>', $extract);
344
+
345
+		return $extract;
346
+	}
347
+
348
+	//--------------------------------------------------------------------
349
+
350
+	/**
351
+	 * Extracts the title from a bit of markdown formatted text. If it doesn't
352
+	 * have an h1 or h2, then it uses the filename.
353
+	 *
354
+	 * @param $excerpt
355
+	 * @param $file
356
+	 * @return string
357
+	 */
358
+	protected function extractTitle($excerpt, $file)
359
+	{
360
+		$title = '';
361
+
362
+		// Easiest to work if this is split into lines.
363
+		$lines = explode("\n", $excerpt);
364
+
365
+		if (is_array($lines) && count($lines)) {
366
+			foreach ($lines as $line) {
367
+				if (strpos($line, '# ') === 0 || strpos($line, '## ') === 0) {
368
+					$title = trim(str_replace('#', '', $line));
369
+					break;
370
+				}
371
+			}
372
+		}
373
+
374
+		// If it's empty, we'll use the filename.
375
+		if (empty($title)) {
376
+			$title = str_replace('_', ' ', $file);
377
+			$title = str_replace('.md', ' ', $title);
378
+			$title = ucwords($title);
379
+		}
380
+
381
+		return $title;
382
+	}
383
+	//--------------------------------------------------------------------
384
+
385
+	/**
386
+	 * Create a Directory Map
387
+	 *
388
+	 * Reads the specified directory and builds an array
389
+	 * representation of it. Sub-folders contained with the
390
+	 * directory will be mapped as well.
391
+	 *
392
+	 * @param    string $source_dir Path to source
393
+	 * @param    int $directory_depth Depth of directories to traverse
394
+	 *                        (0 = fully recursive, 1 = current dir, etc)
395
+	 * @param    bool $hidden Whether to show hidden files
396
+	 * @return    array
397
+	 */
398
+	protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
399
+	{
400
+		if ($fp = @opendir($source_dir)) {
401
+			$filedata = array();
402
+			$new_depth = $directory_depth - 1;
403
+			$source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
404
+
405
+			while (FALSE !== ($file = readdir($fp))) {
406
+				// Remove '.', '..', and hidden files [optional]
407
+				if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) {
408
+					continue;
409
+				}
410
+
411
+				is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR;
412
+
413
+				if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file))
414
+				{
415
+					$filedata[$file] = $this->directory_map($source_dir . $file, $new_depth, $hidden);
416
+				} else
417
+				{
418
+					// Replace the directory separator here with a forward slash since
419
+					// Windows uses backward slashes and not all browsers will auto-replace
420
+					// those slashes in URLs.
421
+					$filedata[] = str_replace(DIRECTORY_SEPARATOR, '/', $file);
422
+				}
423
+			}
424
+
425
+			closedir($fp);
426
+			return $filedata;
427
+		}
428
+
429
+		return FALSE;
430
+	}
431
+
432
+	//--------------------------------------------------------------------
433
+
434
+	/**
435
+	 * Gets the first 'X' words of a string.
436
+	 *
437
+	 * @param $str
438
+	 * @param int $wordCount
439
+	 * @return string
440
+	 */
441
+	protected function firstXWords($str, $wordCount = 10)
442
+	{
443
+		return implode(
444
+			'',
445
+			array_slice(
446
+				preg_split(
447
+					'/([\s,\.;\?\!]+)/',
448
+					$str,
449
+					$wordCount * 2 + 1,
450
+					PREG_SPLIT_DELIM_CAPTURE
451
+				),
452
+				0,
453
+				$wordCount * 2 - 1
454
+			)
455
+		);
456
+	}
457
+
458
+	//--------------------------------------------------------------------
459 459
 
460 460
 }
Please login to merge, or discard this patch.
myth/Events/Events.php 1 patch
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -36,176 +36,176 @@
 block discarded – undo
36 36
 
37 37
 class Events {
38 38
 
39
-    /**
40
-     * The list of listeners.
41
-     *
42
-     * @var array
43
-     */
44
-    protected static $listeners = [];
45
-
46
-    /**
47
-     * Flag to let us know if we've read from the config file
48
-     * and have all of the defined events.
49
-     *
50
-     * @var bool
51
-     */
52
-    protected static $have_read_from_file = false;
53
-
54
-    //--------------------------------------------------------------------
55
-
56
-    /**
57
-     * Registers an action to happen on an event. The action can be any sort
58
-     * of callable:
59
-     *
60
-     *  Events::on('create', 'myFunction');               // procedural function
61
-     *  Events::on('create', ['myClass', 'myMethod']);    // Class::method
62
-     *  Events::on('create', [$myInstance, 'myMethod']);  // Method on an existing instance
63
-     *  Events::on('create', function() {});              // Closure
64
-     *
65
-     * @param $event_name
66
-     * @param callable $callback
67
-     * @param int $priority
68
-     */
69
-    public static function on($event_name, callable $callback, $priority=EVENTS_PRIORITY_NORMAL)
70
-    {
71
-        if (! isset(self::$listeners[$event_name]))
72
-        {
73
-            self::$listeners[$event_name] = [
74
-                true,   // If there's only 1 item, it's sorted.
75
-                [$priority],
76
-                [$callback]
77
-            ];
78
-        }
79
-        else
80
-        {
81
-            self::$listeners[$event_name][0] = false; // Not sorted
82
-            self::$listeners[$event_name][1][] = $priority;
83
-            self::$listeners[$event_name][2][] = $callback;
84
-        }
85
-    }
39
+	/**
40
+	 * The list of listeners.
41
+	 *
42
+	 * @var array
43
+	 */
44
+	protected static $listeners = [];
45
+
46
+	/**
47
+	 * Flag to let us know if we've read from the config file
48
+	 * and have all of the defined events.
49
+	 *
50
+	 * @var bool
51
+	 */
52
+	protected static $have_read_from_file = false;
53
+
54
+	//--------------------------------------------------------------------
55
+
56
+	/**
57
+	 * Registers an action to happen on an event. The action can be any sort
58
+	 * of callable:
59
+	 *
60
+	 *  Events::on('create', 'myFunction');               // procedural function
61
+	 *  Events::on('create', ['myClass', 'myMethod']);    // Class::method
62
+	 *  Events::on('create', [$myInstance, 'myMethod']);  // Method on an existing instance
63
+	 *  Events::on('create', function() {});              // Closure
64
+	 *
65
+	 * @param $event_name
66
+	 * @param callable $callback
67
+	 * @param int $priority
68
+	 */
69
+	public static function on($event_name, callable $callback, $priority=EVENTS_PRIORITY_NORMAL)
70
+	{
71
+		if (! isset(self::$listeners[$event_name]))
72
+		{
73
+			self::$listeners[$event_name] = [
74
+				true,   // If there's only 1 item, it's sorted.
75
+				[$priority],
76
+				[$callback]
77
+			];
78
+		}
79
+		else
80
+		{
81
+			self::$listeners[$event_name][0] = false; // Not sorted
82
+			self::$listeners[$event_name][1][] = $priority;
83
+			self::$listeners[$event_name][2][] = $callback;
84
+		}
85
+	}
86 86
     
87
-    //--------------------------------------------------------------------
88
-
89
-    /**
90
-     * Runs through all subscribed methods running them one at a time,
91
-     * until either:
92
-     *  a) All subscribers have finished or
93
-     *  b) a method returns false, at which point execution of subscribers stops.
94
-     *
95
-     * @param $event_name
96
-     * @return bool
97
-     */
98
-    public static function trigger($event_name, array $arguments = [])
99
-    {
100
-        // Read in our config/events file so that we have them all!
101
-        if (! self::$have_read_from_file)
102
-        {
103
-            if (is_file(APPPATH .'config/events.php'))
104
-            {
105
-                include APPPATH .'config/events.php';
106
-            }
107
-            self::$have_read_from_file = true;
108
-        }
109
-
110
-        foreach (self::listeners($event_name) as $listener)
111
-        {
112
-            $result = call_user_func_array($listener, $arguments);
113
-
114
-            if ($result === false)
115
-            {
116
-                return false;
117
-            }
118
-        }
119
-
120
-        return true;
121
-    }
87
+	//--------------------------------------------------------------------
88
+
89
+	/**
90
+	 * Runs through all subscribed methods running them one at a time,
91
+	 * until either:
92
+	 *  a) All subscribers have finished or
93
+	 *  b) a method returns false, at which point execution of subscribers stops.
94
+	 *
95
+	 * @param $event_name
96
+	 * @return bool
97
+	 */
98
+	public static function trigger($event_name, array $arguments = [])
99
+	{
100
+		// Read in our config/events file so that we have them all!
101
+		if (! self::$have_read_from_file)
102
+		{
103
+			if (is_file(APPPATH .'config/events.php'))
104
+			{
105
+				include APPPATH .'config/events.php';
106
+			}
107
+			self::$have_read_from_file = true;
108
+		}
109
+
110
+		foreach (self::listeners($event_name) as $listener)
111
+		{
112
+			$result = call_user_func_array($listener, $arguments);
113
+
114
+			if ($result === false)
115
+			{
116
+				return false;
117
+			}
118
+		}
119
+
120
+		return true;
121
+	}
122 122
     
123
-    //--------------------------------------------------------------------
124
-
125
-    /**
126
-     * Returns an array of listeners for a single event. They are
127
-     * sorted by priority.
128
-     *
129
-     * If the listener could not be found, returns FALSE, or TRUE if
130
-     * it was removed.
131
-     *
132
-     * @param $event_name
133
-     * @return array
134
-     */
135
-    public static function listeners($event_name)
136
-    {
137
-        if (! isset(self::$listeners[$event_name]))
138
-        {
139
-            return [];
140
-        }
141
-
142
-        // The list is not sorted
143
-        if (! self::$listeners[$event_name][0])
144
-        {
145
-            // Sort it!
146
-            array_multisort(self::$listeners[$event_name][1], SORT_NUMERIC, self::$listeners[$event_name][2]);
147
-
148
-            // Mark it as sorted already!
149
-            self::$listeners[$event_name][0] = true;
150
-        }
151
-
152
-        return self::$listeners[$event_name][2];
153
-    }
154
-
155
-    //--------------------------------------------------------------------
156
-
157
-    /**
158
-     * Removes a single listener from an event.
159
-     *
160
-     * If the listener couldn't be found, returns FALSE, else TRUE if
161
-     * it was removed.
162
-     *
163
-     * @param $event_name
164
-     * @param callable $listener
165
-     * @return bool
166
-     */
167
-    public static function removeListener($event_name, callable $listener)
168
-    {
169
-        if (! isset(self::$listeners[$event_name]))
170
-        {
171
-            return false;
172
-        }
173
-
174
-        foreach (self::$listeners[$event_name][2] as $index => $check)
175
-        {
176
-            if ($check === $listener)
177
-            {
178
-                unset(self::$listeners[$event_name][1][$index]);
179
-                unset(self::$listeners[$event_name][2][$index]);
180
-
181
-                return true;
182
-            }
183
-        }
184
-
185
-        return false;
186
-    }
187
-
188
-    //--------------------------------------------------------------------
189
-
190
-    /**
191
-     * Removes all listeners.
192
-     *
193
-     * If the event_name is specified, only listeners for that event will be
194
-     * removed, otherwise all listeners for all events are removed.
195
-     *
196
-     * @param null $event_name
197
-     */
198
-    public static function removeAllListeners($event_name=null)
199
-    {
200
-        if (! is_null($event_name))
201
-        {
202
-            unset(self::$listeners[$event_name]);
203
-        }
204
-        else {
205
-            self::$listeners = [];
206
-        }
207
-    }
208
-
209
-    //--------------------------------------------------------------------
123
+	//--------------------------------------------------------------------
124
+
125
+	/**
126
+	 * Returns an array of listeners for a single event. They are
127
+	 * sorted by priority.
128
+	 *
129
+	 * If the listener could not be found, returns FALSE, or TRUE if
130
+	 * it was removed.
131
+	 *
132
+	 * @param $event_name
133
+	 * @return array
134
+	 */
135
+	public static function listeners($event_name)
136
+	{
137
+		if (! isset(self::$listeners[$event_name]))
138
+		{
139
+			return [];
140
+		}
141
+
142
+		// The list is not sorted
143
+		if (! self::$listeners[$event_name][0])
144
+		{
145
+			// Sort it!
146
+			array_multisort(self::$listeners[$event_name][1], SORT_NUMERIC, self::$listeners[$event_name][2]);
147
+
148
+			// Mark it as sorted already!
149
+			self::$listeners[$event_name][0] = true;
150
+		}
151
+
152
+		return self::$listeners[$event_name][2];
153
+	}
154
+
155
+	//--------------------------------------------------------------------
156
+
157
+	/**
158
+	 * Removes a single listener from an event.
159
+	 *
160
+	 * If the listener couldn't be found, returns FALSE, else TRUE if
161
+	 * it was removed.
162
+	 *
163
+	 * @param $event_name
164
+	 * @param callable $listener
165
+	 * @return bool
166
+	 */
167
+	public static function removeListener($event_name, callable $listener)
168
+	{
169
+		if (! isset(self::$listeners[$event_name]))
170
+		{
171
+			return false;
172
+		}
173
+
174
+		foreach (self::$listeners[$event_name][2] as $index => $check)
175
+		{
176
+			if ($check === $listener)
177
+			{
178
+				unset(self::$listeners[$event_name][1][$index]);
179
+				unset(self::$listeners[$event_name][2][$index]);
180
+
181
+				return true;
182
+			}
183
+		}
184
+
185
+		return false;
186
+	}
187
+
188
+	//--------------------------------------------------------------------
189
+
190
+	/**
191
+	 * Removes all listeners.
192
+	 *
193
+	 * If the event_name is specified, only listeners for that event will be
194
+	 * removed, otherwise all listeners for all events are removed.
195
+	 *
196
+	 * @param null $event_name
197
+	 */
198
+	public static function removeAllListeners($event_name=null)
199
+	{
200
+		if (! is_null($event_name))
201
+		{
202
+			unset(self::$listeners[$event_name]);
203
+		}
204
+		else {
205
+			self::$listeners = [];
206
+		}
207
+	}
208
+
209
+	//--------------------------------------------------------------------
210 210
 
211 211
 }
Please login to merge, or discard this patch.