Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

md_exporter.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 13.32 KiB


001  <?php
002  /**
003  *
004  * This file is part of the phpBB Forum Software package.
005  *
006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
007  * @license GNU General Public License, version 2 (GPL-2.0)
008  *
009  * For full copyright and license information, please see
010  * the docs/CREDITS.txt file.
011  *
012  */
013   
014  namespace phpbb\event;
015   
016  /**
017  * Crawls through a markdown file and grabs all events
018  */
019  class md_exporter
020  {
021      /** @var string Path where we look for files*/
022      protected $path;
023   
024      /** @var string phpBB Root Path */
025      protected $root_path;
026   
027      /** @var string The minimum version for the events to return */
028      protected $min_version;
029   
030      /** @var string The maximum version for the events to return */
031      protected $max_version;
032   
033      /** @var string */
034      protected $filter;
035   
036      /** @var string */
037      protected $current_event;
038   
039      /** @var array */
040      protected $events;
041   
042      /**
043      * @param string $phpbb_root_path
044      * @param mixed $extension    String 'vendor/ext' to filter, null for phpBB core
045      * @param string $min_version
046      * @param string $max_version
047      */
048      public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null)
049      {
050          $this->root_path = $phpbb_root_path;
051          $this->path = $this->root_path;
052          if ($extension)
053          {
054              $this->path .= 'ext/' . $extension . '/';
055          }
056   
057          $this->events = array();
058          $this->events_by_file = array();
059          $this->filter = $this->current_event = '';
060          $this->min_version = $min_version;
061          $this->max_version = $max_version;
062      }
063   
064      /**
065      * Get the list of all events
066      *
067      * @return array        Array with events: name => details
068      */
069      public function get_events()
070      {
071          return $this->events;
072      }
073   
074      /**
075      * @param string $md_file    Relative from phpBB root
076      * @return int        Number of events found
077      * @throws \LogicException
078      */
079      public function crawl_phpbb_directory_adm($md_file)
080      {
081          $this->crawl_eventsmd($md_file, 'adm');
082   
083          $file_list = $this->get_recursive_file_list($this->path  . 'adm/style/');
084          foreach ($file_list as $file)
085          {
086              $file_name = 'adm/style/' . $file;
087              $this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
088          }
089   
090          return sizeof($this->events);
091      }
092   
093      /**
094      * @param string $md_file    Relative from phpBB root
095      * @return int        Number of events found
096      * @throws \LogicException
097      */
098      public function crawl_phpbb_directory_styles($md_file)
099      {
100          $this->crawl_eventsmd($md_file, 'styles');
101   
102          $styles = array('prosilver');
103          foreach ($styles as $style)
104          {
105              $file_list = $this->get_recursive_file_list(
106                  $this->path . 'styles/' . $style . '/template/'
107              );
108   
109              foreach ($file_list as $file)
110              {
111                  $file_name = 'styles/' . $style . '/template/' . $file;
112                  $this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
113              }
114          }
115   
116          return sizeof($this->events);
117      }
118   
119      /**
120      * @param string $md_file    Relative from phpBB root
121      * @param string $filter        Should be 'styles' or 'adm'
122      * @return int        Number of events found
123      * @throws \LogicException
124      */
125      public function crawl_eventsmd($md_file, $filter)
126      {
127          if (!file_exists($this->path . $md_file))
128          {
129              throw new \LogicException("The event docs file '{$md_file}' could not be found");
130          }
131   
132          $file_content = file_get_contents($this->path . $md_file);
133          $this->filter = $filter;
134   
135          $events = explode("\n\n", $file_content);
136          foreach ($events as $event)
137          {
138              // Last row of the file
139              if (strpos($event, "\n===\n") === false)
140              {
141                  continue;
142              }
143   
144              list($event_name, $details) = explode("\n===\n", $event, 2);
145              $this->validate_event_name($event_name);
146              $this->current_event = $event_name;
147   
148              if (isset($this->events[$this->current_event]))
149              {
150                  throw new \LogicException("The event '{$this->current_event}' is defined multiple times");
151              }
152   
153              if (($this->filter == 'adm' && strpos($this->current_event, 'acp_') !== 0)
154                  || ($this->filter == 'styles' && strpos($this->current_event, 'acp_') === 0))
155              {
156                  continue;
157              }
158   
159              list($file_details, $details) = explode("\n* Since: ", $details, 2);
160   
161              $changed_versions = array();
162              if (strpos($details, "\n* Changed: ") !== false)
163              {
164                  list($since, $details) = explode("\n* Changed: ", $details, 2);
165                  while (strpos($details, "\n* Changed: ") !== false)
166                  {
167                      list($changed, $details) = explode("\n* Changed: ", $details, 2);
168                      $changed_versions[] = $changed;
169                  }
170                  list($changed, $description) = explode("\n* Purpose: ", $details, 2);
171                  $changed_versions[] = $changed;
172              }
173              else
174              {
175                  list($since, $description) = explode("\n* Purpose: ", $details, 2);
176                  $changed_versions = array();
177              }
178   
179              $files = $this->validate_file_list($file_details);
180              $since = $this->validate_since($since);
181              $changes = array();
182              foreach ($changed_versions as $changed)
183              {
184                  list($changed_version, $changed_description) = $this->validate_changed($changed);
185   
186                  if (isset($changes[$changed_version]))
187                  {
188                      throw new \LogicException("Duplicate change information found for event '{$this->current_event}'");
189                  }
190   
191                  $changes[$changed_version] = $changed_description;
192              }
193              $description = trim($description, "\n") . "\n";
194   
195              if (!$this->version_is_filtered($since))
196              {
197                  $is_filtered = false;
198                  foreach ($changes as $version => $null)
199                  {
200                      if ($this->version_is_filtered($version))
201                      {
202                          $is_filtered = true;
203                          break;
204                      }
205                  }
206   
207                  if (!$is_filtered)
208                  {
209                      continue;
210                  }
211              }
212   
213              $this->events[$event_name] = array(
214                  'event'            => $this->current_event,
215                  'files'            => $files,
216                  'since'            => $since,
217                  'changed'        => $changes,
218                  'description'    => $description,
219              );
220          }
221   
222          return sizeof($this->events);
223      }
224   
225      /**
226       * The version to check
227       *
228       * @param string $version
229       * @return bool
230       */
231      protected function version_is_filtered($version)
232      {
233          return (!$this->min_version || phpbb_version_compare($this->min_version, $version, '<='))
234          && (!$this->max_version || phpbb_version_compare($this->max_version, $version, '>='));
235      }
236   
237      /**
238      * Format the php events as a wiki table
239      *
240      * @param string $action
241      * @return string        Number of events found
242      */
243      public function export_events_for_wiki($action = '')
244      {
245          if ($this->filter === 'adm')
246          {
247              if ($action === 'diff')
248              {
249                  $wiki_page = '=== ACP Template Events ===' . "\n";
250              }
251              else
252              {
253                  $wiki_page = '= ACP Template Events =' . "\n";
254              }
255              $wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
256              $wiki_page .= '! Identifier !! Placement !! Added in Release !! Explanation' . "\n";
257          }
258          else
259          {
260              if ($action === 'diff')
261              {
262                  $wiki_page = '=== Template Events ===' . "\n";
263              }
264              else
265              {
266                  $wiki_page = '= Template Events =' . "\n";
267              }
268              $wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
269              $wiki_page .= '! Identifier !! Prosilver Placement (If applicable) !! Added in Release !! Explanation' . "\n";
270          }
271   
272          foreach ($this->events as $event_name => $event)
273          {
274              $wiki_page .= "|- id=\"{$event_name}\"\n";
275              $wiki_page .= "| [[#{$event_name}|{$event_name}]] || ";
276   
277              if ($this->filter === 'adm')
278              {
279                  $wiki_page .= implode(', ', $event['files']['adm']);
280              }
281              else
282              {
283                  $wiki_page .= implode(', ', $event['files']['prosilver']);
284              }
285   
286              $wiki_page .= " || {$event['since']} || " . str_replace("\n", ' ', $event['description']) . "\n";
287          }
288          $wiki_page .= '|}' . "\n";
289   
290          return $wiki_page;
291      }
292   
293      /**
294      * Validates a template event name
295      *
296      * @param $event_name
297      * @return null
298      * @throws \LogicException
299      */
300      public function validate_event_name($event_name)
301      {
302          if (!preg_match('#^([a-z][a-z0-9]*(?:_[a-z][a-z0-9]*)+)$#', $event_name))
303          {
304              throw new \LogicException("Invalid event name '{$event_name}'");
305          }
306      }
307   
308      /**
309      * Validate "Since" Information
310      *
311      * @param string $since
312      * @return string
313      * @throws \LogicException
314      */
315      public function validate_since($since)
316      {
317          if (!$this->validate_version($since))
318          {
319              throw new \LogicException("Invalid since information found for event '{$this->current_event}'");
320          }
321   
322          return $since;
323      }
324   
325      /**
326      * Validate "Changed" Information
327      *
328      * @param string $changed
329      * @return string
330      * @throws \LogicException
331      */
332      public function validate_changed($changed)
333      {
334          if (strpos($changed, ' ') !== false)
335          {
336              list($version, $description) = explode(' ', $changed, 2);
337          }
338          else
339          {
340              $version = $changed;
341              $description = '';
342          }
343   
344          if (!$this->validate_version($version))
345          {
346              throw new \LogicException("Invalid changed information found for event '{$this->current_event}'");
347          }
348   
349          return array($version, $description);
350      }
351   
352      /**
353      * Validate "version" Information
354      *
355      * @param string $version
356      * @return bool True if valid, false otherwise
357      */
358      public function validate_version($version)
359      {
360          return preg_match('#^\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?$#', $version);
361      }
362   
363      /**
364      * Validate the files list
365      *
366      * @param string $file_details
367      * @return array
368      * @throws \LogicException
369      */
370      public function validate_file_list($file_details)
371      {
372          $files_list = array(
373              'prosilver'        => array(),
374              'adm'            => array(),
375          );
376   
377          // Multi file list
378          if (strpos($file_details, "* Locations:\n    + ") === 0)
379          {
380              $file_details = substr($file_details, strlen("* Locations:\n    + "));
381              $files = explode("\n    + ", $file_details);
382              foreach ($files as $file)
383              {
384                  if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
385                  {
386                      throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 1);
387                  }
388   
389                  if (($this->filter !== 'adm') && strpos($file, 'styles/prosilver/template/') === 0)
390                  {
391                      $files_list['prosilver'][] = substr($file, strlen('styles/prosilver/template/'));
392                  }
393                  else if (($this->filter === 'adm') && strpos($file, 'adm/style/') === 0)
394                  {
395                      $files_list['adm'][] = substr($file, strlen('adm/style/'));
396                  }
397                  else
398                  {
399                      throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 2);
400                  }
401   
402                  $this->events_by_file[$file][] = $this->current_event;
403              }
404          }
405          else if ($this->filter == 'adm')
406          {
407              $file = substr($file_details, strlen('* Location: '));
408              if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
409              {
410                  throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 1);
411              }
412   
413              $files_list['adm'][] =  substr($file, strlen('adm/style/'));
414   
415              $this->events_by_file[$file][] = $this->current_event;
416          }
417          else
418          {
419              throw new \LogicException("Invalid file list found for event '{$this->current_event}'", 2);
420          }
421   
422          return $files_list;
423      }
424   
425      /**
426      * Get all template events in a template file
427      *
428      * @param string $file
429      * @return array
430      * @throws \LogicException
431      */
432      public function crawl_file_for_events($file)
433      {
434          if (!file_exists($this->path . $file))
435          {
436              throw new \LogicException("File '{$file}' does not exist", 1);
437          }
438   
439          $event_list = array();
440          $file_content = file_get_contents($this->path . $file);
441   
442          $events = explode('<!-- EVENT ', $file_content);
443          // Remove the code before the first event
444          array_shift($events);
445          foreach ($events as $event)
446          {
447              $event = explode(' -->', $event, 2);
448              $event_list[] = array_shift($event);
449          }
450   
451          return $event_list;
452      }
453   
454      /**
455      * Validates whether all events from $file are in the md file and vice-versa
456      *
457      * @param string $file
458      * @param array $events
459      * @return true
460      * @throws \LogicException
461      */
462      public function validate_events_from_file($file, array $events)
463      {
464          if (empty($this->events_by_file[$file]) && empty($events))
465          {
466              return true;
467          }
468          else if (empty($this->events_by_file[$file]))
469          {
470              $event_list = implode("', '", $events);
471              throw new \LogicException("File '{$file}' should not contain events, but contains: "
472                  . "'{$event_list}'", 1);
473          }
474          else if (empty($events))
475          {
476              $event_list = implode("', '", $this->events_by_file[$file]);
477              throw new \LogicException("File '{$file}' contains no events, but should contain: "
478                  . "'{$event_list}'", 1);
479          }
480   
481          $missing_events_from_file = array();
482          foreach ($this->events_by_file[$file] as $event)
483          {
484              if (!in_array($event, $events))
485              {
486                  $missing_events_from_file[] = $event;
487              }
488          }
489   
490          if (!empty($missing_events_from_file))
491          {
492              $event_list = implode("', '", $missing_events_from_file);
493              throw new \LogicException("File '{$file}' does not contain events: '{$event_list}'", 2);
494          }
495   
496          $missing_events_from_md = array();
497          foreach ($events as $event)
498          {
499              if (!in_array($event, $this->events_by_file[$file]))
500              {
501                  $missing_events_from_md[] = $event;
502              }
503          }
504   
505          if (!empty($missing_events_from_md))
506          {
507              $event_list = implode("', '", $missing_events_from_md);
508              throw new \LogicException("File '{$file}' contains additional events: '{$event_list}'", 3);
509          }
510   
511          return true;
512      }
513   
514      /**
515      * Returns a list of files in $dir
516      *
517      * Works recursive with any depth
518      *
519      * @param    string    $dir    Directory to go through
520      * @return    array    List of files (including directories)
521      */
522      public function get_recursive_file_list($dir)
523      {
524          try
525          {
526              $iterator = new \RecursiveIteratorIterator(
527                  new \phpbb\recursive_dot_prefix_filter_iterator(
528                      new \RecursiveDirectoryIterator(
529                          $dir,
530                          \FilesystemIterator::SKIP_DOTS
531                      )
532                  ),
533                  \RecursiveIteratorIterator::SELF_FIRST
534              );
535          }
536          catch (\Exception $e)
537          {
538              return array();
539          }
540   
541          $files = array();
542          foreach ($iterator as $file_info)
543          {
544              /** @var \RecursiveDirectoryIterator $file_info */
545              if ($file_info->isDir())
546              {
547                  continue;
548              }
549   
550              $relative_path = $iterator->getInnerIterator()->getSubPathname();
551   
552              if (substr($relative_path, -5) == '.html')
553              {
554                  $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path);
555              }
556          }
557   
558          return $files;
559      }
560  }
561