├── .circleci └── config.yml ├── LICENSE ├── action.php ├── conf ├── default.php └── metadata.php ├── fileHelper ├── fileHelper.php ├── filePreparer.php ├── namespacePreparer.php └── pagePreparer.php ├── images └── tb_nspages.png ├── lang ├── ca │ └── lang.php ├── cs │ └── lang.php ├── de │ ├── lang.php │ └── settings.php ├── el │ ├── lang.php │ └── settings.php ├── en │ ├── lang.php │ └── settings.php ├── eo │ └── lang.php ├── es │ ├── lang.php │ └── settings.php ├── fa │ ├── lang.php │ └── settings.php ├── fr │ ├── lang.php │ └── settings.php ├── hr │ ├── lang.php │ └── settings.php ├── hu │ ├── lang.php │ └── settings.php ├── ja │ ├── lang.php │ └── settings.php ├── ko │ ├── lang.php │ └── settings.php ├── nl │ ├── lang.php │ └── settings.php ├── no │ ├── lang.php │ └── settings.php ├── pt-br │ ├── lang.php │ └── settings.php ├── ru │ ├── lang.php │ └── settings.php ├── sv │ ├── lang.php │ └── settings.php ├── uk │ ├── lang.php │ └── settings.php ├── vi │ ├── lang.php │ └── settings.php └── zh │ ├── lang.php │ └── settings.php ├── namespaceFinder.php ├── optionParser.php ├── plugin.info.txt ├── printers ├── printer.php ├── printerLineBreak.php ├── printerNice.php ├── printerOneLine.php ├── printerPictures.php ├── printerSimpleList.php ├── printerTree.php ├── rendererXhtmlHelper.php └── sorters.php ├── style.css └── syntax.php /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # PHP CircleCI 2.0 configuration file 2 | # See: https://circleci.com/docs/2.0/language-php/ 3 | version: 2 4 | 5 | # Define a job to be invoked later in a workflow. 6 | # See: https://circleci.com/docs/2.0/configuration-reference/#jobs 7 | jobs: 8 | build: 9 | # Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. 10 | # See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor 11 | docker: 12 | - image: cimg/php:8.2.8-browsers 13 | 14 | # Add steps to the job 15 | # See: https://circleci.com/docs/2.0/configuration-reference/#steps 16 | steps: 17 | - checkout 18 | # Download and cache dependencies 19 | - restore_cache: 20 | keys: 21 | - v1-dependencies-{{ checksum "_tests/source.sh" }} 22 | - run: sudo apt-get update 23 | - run: sudo apt install maven 24 | - run: 25 | command: ./_tests/run-tests.sh 26 | environment: 27 | MOZ_HEADLESS: 1 28 | - save_cache: 29 | key: v1-dependencies-{{ checksum "_tests/source.sh" }} 30 | paths: 31 | - ./_tests/dw_dl_cache/ 32 | - store_artifacts: 33 | path: ./_tests/selenium_screenshot.png 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /action.php: -------------------------------------------------------------------------------- 1 | register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'insert_button', array()); 10 | $controller->register_hook('PLUGIN_POPULARITY_DATA_SETUP', 'AFTER', $this, 'usage_data'); 11 | } 12 | 13 | function insert_button(& $event, $param) { 14 | $event->data[] = array ( 15 | 'type' => 'insert', 16 | 'title' => 'nspages', 17 | 'icon' => '../../plugins/nspages/images/tb_nspages.png', 18 | 'insert' => $this->getConf('toolbar_inserted_markup') 19 | ); 20 | } 21 | 22 | function usage_data(&$event){ 23 | $plugin_info = $this->getInfo(); 24 | $event->data['nspages']['version'] = $plugin_info['date']; 25 | $event->data['nspages']['legacyModificationDateSyntax'] = $this->used_legacy_syntax_not_too_long_ago() ? 'true' : 'false'; 26 | } 27 | 28 | private function used_legacy_syntax_not_too_long_ago(){ 29 | $legacySyntax = io_readFile(action_plugin_nspages::legacySyntaxFilename()); 30 | if ($legacySyntax){ 31 | if ($legacySyntax > time() - 365 * 86400){ 32 | return true; 33 | } else { 34 | unlink(action_plugin_nspages::legacySyntaxFilename()); 35 | } 36 | } 37 | } 38 | 39 | static function logUseLegacySyntax(){ 40 | $file = action_plugin_nspages::legacySyntaxFilename(); 41 | io_saveFile($file, time()); 42 | } 43 | 44 | static function legacySyntaxFilename(){ 45 | global $conf; 46 | return $conf['savedir'] . '/nspages_legacy_syntax.txt'; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /conf/default.php: -------------------------------------------------------------------------------- 1 | '; 4 | $conf['default_picture'] = ''; 5 | $conf['cache'] = 1; 6 | $conf['custom_title_allow_list_metadata'] = 'title, user, date.created'; 7 | $conf['global_exclude'] = '_template,__template,c_template,i_template'; 8 | -------------------------------------------------------------------------------- /conf/metadata.php: -------------------------------------------------------------------------------- 1 | data = $data; 19 | $this->files = $this->searchFiles($data); 20 | $this->customTitleAllowListMetadata = $customTitleAllowListMetadata; 21 | } 22 | 23 | private function searchFiles(){ 24 | global $conf; 25 | $opt = array( 26 | 'depth' => $this->data['maxDepth'], 'keeptxt'=> false, 'listfiles'=> !$this->data['nopages'], 27 | 'listdirs' => $this->data['subns'], 'pagesonly'=> true, 'skipacl'=> false, 28 | 'sneakyacl' => true, 'hash'=> false, 'meta'=> true, 'showmsg'=> false, 29 | 'showhidden'=> $this->data['showhidden'], 'firsthead'=> true 30 | ); 31 | $files = array(); 32 | search($files, $conf['datadir'], 'search_universal', $opt, $this->data['wantedDir']); 33 | return $files; 34 | } 35 | 36 | function getPages(){ 37 | $preparer = new pagePreparer($this->data['excludedNS'], $this->data['excludedPages'], $this->data['pregPagesOn'], 38 | $this->data['pregPagesOff'], $this->data['pregPagesTitleOn'], $this->data['pregPagesTitleOff'], $this->data['title'], 39 | $this->data['sortid'], $this->data['idAndTitle'], $this->data['sortDate'], $this->data['sortByCreationDate'], 40 | $this->data['customTitle'], $this->customTitleAllowListMetadata, $this->data['sortByMetadata'], 41 | $this->data['excludeSelfPage']); 42 | return $this->getFiles($preparer); 43 | } 44 | 45 | function getSubnamespaces(){ 46 | $preparer = new namespacePreparer($this->data['excludedNS'], $this->data['pregNSOn'], $this->data['pregNSOff'], 47 | $this->data['pregNSTitleOn'], $this->data['pregNSTitleOff'], $this->data['title'], $this->data['sortid'], 48 | $this->data['idAndTitle'], $this->data['sortDate'], $this->data['sortByCreationDate']); 49 | return $this->getFiles($preparer); 50 | } 51 | 52 | private function getFiles($preparer){ 53 | $files = array(); 54 | foreach($this->files as $item) { 55 | $preparer->prepareFileTitle($item); 56 | if($preparer->isFileWanted($item, false) && $preparer->isFileWanted($item, true)) { 57 | $preparer->prepareFile($item); 58 | $files[] = $item; 59 | } 60 | } 61 | return $files; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /fileHelper/filePreparer.php: -------------------------------------------------------------------------------- 1 | excludedFiles = $excludedFiles; 40 | $this->pregOn = $pregOn; 41 | $this->pregOff = $pregOff; 42 | $this->pregTitleOn = $pregTitleOn; 43 | $this->pregTitleOff = $pregTitleOff; 44 | $this->useTitle = $useTitle; 45 | $this->sortPageById = $sortPageById; 46 | $this->useIdAndTitle = $useIdAndTitle; 47 | $this->sortPageByDate = $sortPageByDate; 48 | $this->sortByCreationDate = $sortByCreationDate; 49 | } 50 | 51 | function isFileWanted($file, $useTitle) { 52 | $nameToFilterOn = $useTitle ? $file['title'] : noNS($file['id']); 53 | $pregOn = $useTitle ? $this->pregTitleOn : $this->pregOn; 54 | $pregOff = $useTitle ? $this->pregTitleOff : $this->pregOff; 55 | 56 | if (in_array($nameToFilterOn, $this->excludedFiles)) { 57 | return false; 58 | } 59 | foreach($pregOn as $preg) { 60 | if (!preg_match($preg, $nameToFilterOn)) { 61 | return false; 62 | } 63 | } 64 | foreach($pregOff as $preg) { 65 | if (preg_match($preg, $nameToFilterOn)) { 66 | return false; 67 | } 68 | } 69 | return true; 70 | } 71 | 72 | abstract function prepareFile(&$file); 73 | abstract function prepareFileTitle(&$file); 74 | } 75 | -------------------------------------------------------------------------------- /fileHelper/namespacePreparer.php: -------------------------------------------------------------------------------- 1 | getMainPageId($ns); 24 | if ( !is_null($idMainPage) ){ 25 | $ns['title'] = p_get_first_heading($idMainPage, true); 26 | } else { 27 | $ns['title'] = null; 28 | } 29 | } 30 | 31 | /** 32 | * When we display a namespace, we want to: 33 | * - link to it's main page (if such a page exists) 34 | * - get the id of this main page (if the option is active) 35 | * 36 | * @param $ns A structure which represents a namespace 37 | */ 38 | function prepareFile(&$ns){ 39 | $ns['nameToDisplay'] = $this->buildNameToDisplay($ns['title'], noNS($ns['id'])); 40 | $ns['id'] = $this->buildIdToLinkTo($this->getMainPageId($ns), $ns['id']); 41 | $ns['sort'] = $this->buildSortAttribute($ns['nameToDisplay'], $ns['id'], $ns['mtime']); 42 | } 43 | 44 | private function getMainPageId(&$ns){ 45 | if (!array_key_exists('idMainPage', $ns)){ 46 | $idMainPage = $ns['id'].':'; 47 | // Get the id of the namespace's main page 48 | $resolver = new PageResolver($idMainPage); 49 | $page = $resolver->resolveId($idMainPage); 50 | $ns['idMainPage'] = page_exists($page) ? $page : null; 51 | } 52 | return $ns['idMainPage']; 53 | } 54 | 55 | private function buildNameToDisplay($title, $defaultName){ 56 | if ( ! is_null($title) ){ 57 | if($this->useIdAndTitle){ 58 | return $defaultName . " - " . $title; 59 | } 60 | 61 | if($this->useTitle) { 62 | return $title; 63 | } 64 | } 65 | 66 | return $defaultName; 67 | } 68 | 69 | private function buildIdToLinkTo($idMainPage, $currentNsId){ 70 | if(is_null($idMainPage)) { 71 | return $currentNsId . ':'; 72 | } else { 73 | return $idMainPage; 74 | } 75 | } 76 | 77 | private function buildSortAttribute($nameToDisplay, $nsId, $mtime){ 78 | if ( $this->sortPageById ){ 79 | return curNS($nsId); 80 | } else if ( $this->sortPageByDate ){ 81 | return $mtime; 82 | } else { 83 | return $nameToDisplay; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /fileHelper/pagePreparer.php: -------------------------------------------------------------------------------- 1 | excludedNs = $excludedNs; 28 | $this->customTitle = $customTitle; 29 | $this->customTitleAllowListMetadata = $customTitleAllowListMetadata; 30 | $this->sortByMetadata = $sortByMetadata; 31 | $this->excludeSelfPage = $excludeSelfPage; 32 | } 33 | 34 | function isFileWanted($file, $useTitle){ 35 | global $ID; 36 | return ($file['type'] != 'd') 37 | && parent::isFileWanted($file, $useTitle) 38 | && $this->passSubNsfilterInRecursiveMode($file) 39 | && (!$this->excludeSelfPage || $ID !== $file['id']); 40 | } 41 | 42 | function prepareFileTitle(&$file){ 43 | // Nothing to do: for pages the title is already set 44 | } 45 | 46 | private function passSubNsfilterInRecursiveMode($file){ 47 | $subNss = explode(':', $file['id']); 48 | if ( count($subNss) < 2 ){ //It means we're not in recursive mode 49 | return true; 50 | } 51 | for ($i = 0; $i < count($subNss) - 1; $i++) { 52 | if (in_array($subNss[$i], $this->excludedNs)) { 53 | return false; 54 | } 55 | } 56 | return true; 57 | } 58 | 59 | function prepareFile(&$page){ 60 | $page['nameToDisplay'] = $this->buildNameToDisplay($page); 61 | $page['sort'] = $this->buildSortAttribute($page['nameToDisplay'], $page['id'], $page['mtime']); 62 | } 63 | 64 | /** 65 | * Get the a metadata value from a certain path. 66 | * 67 | * @param $metadata - The metadata object of a page. More details on https://www.dokuwiki.org/devel:metadata 68 | * @param $path - The path. 69 | * Examples: 70 | * date.created 71 | * contributor.0 72 | * 73 | * @return mixed - The metadata value from a certain path. 74 | */ 75 | 76 | private function getMetadataFromPath($metadata, $path) { 77 | return array_reduce( 78 | explode('.', $path), 79 | function ($object, $property) { 80 | return is_numeric($property) ? $object[$property] : $object[$property]; 81 | }, 82 | $metadata 83 | ); 84 | } 85 | 86 | private function isPathInMetadataAllowList($path) { 87 | $metadataAllowList = explode(',', preg_replace('/\s+/', '', $this->customTitleAllowListMetadata)); 88 | return in_array($path, $metadataAllowList); 89 | } 90 | 91 | /** 92 | * Get the page custom title from a template. 93 | * 94 | * @param $customTitle - The custom tile template. 95 | * Examples: 96 | * {title} ({data.created} by {user}) 97 | * @param $metadata - The metadata object of a page. More details on https://www.dokuwiki.org/devel:metadata 98 | * 99 | * @return string - the custom title 100 | */ 101 | 102 | private function getCustomTitleFromTemplate($customTitle, $metadata) { 103 | return preg_replace_callback( 104 | '/{(.*?)}/', 105 | function ($matches) use($metadata) { 106 | $path = $matches[1]; 107 | if ($this->isPathInMetadataAllowList($path)) { 108 | return $this->getMetadataFromPath($metadata, $path); 109 | } else { 110 | return $path; 111 | } 112 | }, 113 | $customTitle 114 | ); 115 | } 116 | 117 | private function buildNameToDisplay($page){ 118 | $title = $page['title']; 119 | $pageId = $page['id']; 120 | 121 | 122 | if ($this->customTitle !== null) { 123 | $meta = p_get_metadata($pageId); 124 | return $this->getCustomTitleFromTemplate($this->customTitle, $meta); 125 | } 126 | 127 | if($this->useIdAndTitle && $title !== null ){ 128 | return noNS($pageId) . " - " . $title; 129 | } 130 | 131 | if(!$this->useTitle || $title === null) { 132 | return noNS($pageId); 133 | } 134 | return $title; 135 | } 136 | 137 | private function buildSortAttribute($nameToDisplay, $pageId, $mtime){ 138 | if ($this->sortByMetadata !== null) { 139 | $meta = p_get_metadata($pageId); 140 | return $this->getMetadataFromPath($meta, $this->sortByMetadata); 141 | } else if($this->sortPageById) { 142 | return noNS($pageId); 143 | } else if ( $this->sortPageByDate) { 144 | return $mtime; 145 | } else if ($this->sortByCreationDate) { 146 | $meta = p_get_metadata($pageId); 147 | return $meta['date']['created']; 148 | } else { 149 | return $nameToDisplay; 150 | } 151 | 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /images/tb_nspages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gturri/nspages/bee842cb86ee13a99570c37ec94969bfbb9f2ea9/images/tb_nspages.png -------------------------------------------------------------------------------- /lang/ca/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | $lang['encoding'] = 'utf-8'; 10 | $lang['direction'] = 'ltr'; 11 | $lang['doesntexist'] = 'aquest namespace no existeix: '; 12 | $lang['subcats'] = 'Subnamespaces:'; 13 | $lang['pagesinthiscat'] = 'Pàgines en aquest namespace:'; 14 | $lang['continued'] = ' cont.'; 15 | $lang['nopages'] = 'No hi ha pàgines en aquest namespace.'; 16 | $lang['nosubns'] = 'No hi ha subnamespaces.'; 17 | -------------------------------------------------------------------------------- /lang/cs/lang.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Sebastian Engel 8 | * @author Daniel Schranz 9 | */ 10 | $lang['encoding'] = 'utf-8'; 11 | $lang['direction'] = 'ltr'; 12 | $lang['doesntexist'] = 'Dieser Namensraum existiert nicht: '; 13 | $lang['subcats'] = 'Unternamensraum:'; 14 | $lang['pagesinthiscat'] = 'Seiten in diesem Namensraum:'; 15 | $lang['continued'] = ' (Fortsetzung)'; 16 | $lang['nopages'] = 'Keine Seiten in diesem Namensraum.'; 17 | $lang['nosubns'] = 'Keine Unternamensräume.'; 18 | $lang['sidebarOrNs'] = 'Mit der -sidebar Option können Sie keinen namespace definieren. 19 | Gefunden:'; 20 | -------------------------------------------------------------------------------- /lang/de/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Sebastian Engel 8 | * @author Padhie 9 | */ 10 | $lang['cache'] = 'Cache deaktivieren'; 11 | $lang['default_picture'] = 'Lege dein Standardbild fest'; 12 | $lang['toolbar_inserted_markup'] = 'Toolbar eingefügtes markup'; 13 | $lang['custom_title_allow_list_metadata'] = 'Benutzerdefinierter Titel ermöglicht Metadatenliste'; 14 | $lang['global_exclude'] = 'Globale Ausschließungsliste für Seiten und Namensräume'; 15 | -------------------------------------------------------------------------------- /lang/el/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | $lang['encoding'] = 'utf-8'; 10 | $lang['direction'] = 'ltr'; 11 | $lang['doesntexist'] = 'αυτός ο φάκελος δεν υπάρχει: '; 12 | $lang['subcats'] = 'Υποφάκελοι:'; 13 | $lang['pagesinthiscat'] = 'Σελίδες σε αυτό το φάκελο:'; 14 | $lang['continued'] = ' συνέχεια'; 15 | $lang['nopages'] = 'Δεν υπάρχουν σελίδες σε αυτό το φάκελο.'; 16 | $lang['nosubns'] = 'Δεν υπάρχουν υποφάκελοι.'; 17 | -------------------------------------------------------------------------------- /lang/el/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | $lang['encoding'] = 'utf-8'; 10 | $lang['direction'] = 'ltr'; 11 | $lang['doesntexist'] = 'this namespace doesn\'t exist: '; 12 | $lang['subcats'] = 'Subnamespaces:'; 13 | $lang['pagesinthiscat'] = 'Pages in this namespace:'; 14 | $lang['continued'] = ' cont.'; 15 | $lang['nopages'] = 'No pages in this namespace.'; 16 | $lang['nosubns'] = 'No subnamespaces.'; 17 | $lang['sidebarOrNs'] = 'With the -sidebar option you cannot specify a namespace. Found: '; 18 | -------------------------------------------------------------------------------- /lang/en/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'tiu nomspaco ne ekzistas:'; 11 | $lang['subcats'] = 'Subnomspaco:'; 12 | $lang['pagesinthiscat'] = 'Paĝoj en tiu nomspaco:'; 13 | $lang['continued'] = 'pli'; 14 | $lang['nopages'] = 'Neniuj paĝoj en tiu nomspaco.'; 15 | $lang['nosubns'] = 'Neniu subnomspaco.'; 16 | -------------------------------------------------------------------------------- /lang/es/lang.php: -------------------------------------------------------------------------------- 1 | 7 | * @updater Daniel D. Rodrigues 8 | */ 9 | 10 | $lang['encoding'] = 'utf-8'; 11 | $lang['direction'] = 'ltr'; 12 | $lang['doesntexist'] = 'esta sección no existe: '; 13 | $lang['subcats'] = 'Sección subordinadas:'; 14 | $lang['pagesinthiscat'] = 'Páginas en esta sección:'; 15 | $lang['continued'] = ' cont.'; 16 | $lang['nopages'] = 'No hay páginas en esta sección.'; 17 | $lang['nosubns'] = 'No hay sección subordinadas.'; 18 | -------------------------------------------------------------------------------- /lang/es/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @updater Daniel D. Rodrigues 8 | */ 9 | 10 | $lang['cache'] = 'Deshabilitar cache'; 11 | $lang['default_picture'] = 'Configura tu imagen predeterminada'; 12 | $lang['toolbar_inserted_markup'] = 'Texto al utilizar botón de herramientas'; 13 | $lang['custom_title_allow_list_metadata'] = 'Lista de metadatos permitidos para la opción -customTitle'; 14 | $lang['global_exclude'] = 'Lista de exclusión global para páginas y secciones'; 15 | -------------------------------------------------------------------------------- /lang/fa/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'این فضای‌نام وجود ندارد:'; 11 | $lang['subcats'] = 'فضای‌نام‌های فرعی:'; 12 | $lang['pagesinthiscat'] = 'صفحات در این فضای‌نام:'; 13 | $lang['continued'] = 'ادامه.'; 14 | $lang['nopages'] = 'صفحه‌ای در این فضای‌نام نیست.'; 15 | $lang['nosubns'] = 'بدون فضای‌نام‌های فرعی.'; 16 | -------------------------------------------------------------------------------- /lang/fa/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Ghassem Tofighi 8 | */ 9 | $lang['cache'] = 'غیرفعال کردن کش '; 10 | $lang['default_picture'] = 'درج تصویر پیش‌فرض'; 11 | $lang['toolbar_inserted_markup'] = 'درج نشانه‌گذار نوار ابزار'; 12 | 13 | -------------------------------------------------------------------------------- /lang/fr/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | $lang['encoding'] = 'utf-8'; 10 | $lang['direction'] = 'ltr'; 11 | $lang['doesntexist'] = 'cette catégorie n\'existe pas : '; 12 | $lang['subcats'] = 'Sous-catégories :'; 13 | $lang['pagesinthiscat'] = 'Pages dans la catégorie :'; 14 | $lang['continued'] = ' (suite)'; 15 | $lang['nopages'] = 'Pas de pages dans cette catégorie.'; 16 | $lang['nosubns'] = 'Pas de sous-catégories.'; 17 | $lang['sidebarOrNs'] = 'Avec l\'option -sidebar il n\'est pas possible de spécifier de catégorie. Trouvé: '; 18 | -------------------------------------------------------------------------------- /lang/fr/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'ovaj imenski prostor ne postoji'; 11 | $lang['subcats'] = 'Pod-imenski prostori:'; 12 | $lang['pagesinthiscat'] = 'Stranice u ovom imenskom prostoru:'; 13 | $lang['continued'] = 'nastavi...'; 14 | $lang['nopages'] = 'Nema stranica u ovom imenskom prostoru.'; 15 | $lang['nosubns'] = 'Nema pod-imenskih prostora.'; 16 | -------------------------------------------------------------------------------- /lang/hr/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = 'Onemogući međuspremnik'; 9 | $lang['default_picture'] = 'Postavi svoju podrazumijevanu sliku'; 10 | $lang['toolbar_inserted_markup'] = 'Oznaka za umetnutu alatnu traku'; 11 | -------------------------------------------------------------------------------- /lang/hu/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'A névtér nem létezik: '; 11 | $lang['subcats'] = 'Al-névtér:'; 12 | $lang['pagesinthiscat'] = 'Oldalak a névtérben:'; 13 | $lang['continued'] = ' folyt.'; 14 | $lang['nopages'] = 'A névtér nem tartalmaz oldalakat.'; 15 | $lang['nosubns'] = 'Nincs al-névtér.'; 16 | -------------------------------------------------------------------------------- /lang/hu/settings.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | $lang['cache'] = 'Gyorsítótár kikapcsolása'; 9 | $lang['toolbar_inserted_markup'] = 'Eszköztárból beilleszthető kódrészlet'; 10 | -------------------------------------------------------------------------------- /lang/ja/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'この名前空間は存在しません:'; 11 | $lang['subcats'] = 'サブ名前空間:'; 12 | $lang['pagesinthiscat'] = 'この名前空間のページ:'; 13 | $lang['continued'] = ' の続き'; 14 | $lang['nopages'] = 'この名前空間にページがありません。'; 15 | $lang['nosubns'] = 'サブ名前空間がありません。'; 16 | -------------------------------------------------------------------------------- /lang/ja/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Hideaki SAWADA 8 | */ 9 | $lang['cache'] = 'キャッシュを無効にする。'; 10 | $lang['default_picture'] = '規定の画像を設定する'; 11 | $lang['toolbar_inserted_markup'] = '構文を挿入するツールバー'; 12 | -------------------------------------------------------------------------------- /lang/ko/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = '이 이름공간은 존재하지 않습니다:'; 11 | $lang['subcats'] = '하위 이름공간:'; 12 | $lang['pagesinthiscat'] = '이 이름공간에 있는 문서:'; 13 | $lang['continued'] = '(계속)'; 14 | $lang['nopages'] = '이 이름공간에 문서가 없습니다.'; 15 | $lang['nosubns'] = '하위 이름공간이 없습니다.'; 16 | -------------------------------------------------------------------------------- /lang/ko/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Myeongjin 8 | */ 9 | $lang['cache'] = '캐시를 비활성화'; 10 | $lang['toolbar_inserted_markup'] = '마크업이 삽입된 툴바'; 11 | -------------------------------------------------------------------------------- /lang/nl/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'deze naamruimte bestaat niet:'; 11 | $lang['subcats'] = 'Subnaamruimte:'; 12 | $lang['pagesinthiscat'] = 'Pagina\'s in deze naamruimte:'; 13 | $lang['continued'] = 'verder'; 14 | $lang['nopages'] = 'Geen pagina\'s in deze naamruimte.'; 15 | $lang['nosubns'] = 'Geen subnaamruimte.'; 16 | -------------------------------------------------------------------------------- /lang/nl/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author hugo smet 8 | * @author Mark C. Prins 9 | */ 10 | $lang['cache'] = 'uitschakelen tijdelijk geheugen (cache)'; 11 | $lang['default_picture'] = 'Stel je standaardafbeelding in'; 12 | $lang['toolbar_inserted_markup'] = 'Door de toolbarknop ingevoegde code'; 13 | $lang['custom_title_allow_list_metadata'] = 'Aangepaste titel mag metadata laten zien'; 14 | $lang['global_exclude'] = 'Globale uitsluiting van pagina\'s en naamruimtes'; 15 | -------------------------------------------------------------------------------- /lang/no/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'dette navnerommet fins ikke:'; 11 | $lang['subcats'] = 'Undernavnerom:'; 12 | $lang['pagesinthiscat'] = 'Sider i dette navnerommet:'; 13 | $lang['continued'] = 'forts.'; 14 | $lang['nopages'] = 'Ingen sider i dette navnerommet.'; 15 | $lang['nosubns'] = 'Ingen undernavnerom.'; 16 | -------------------------------------------------------------------------------- /lang/no/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = 'Slå av hurtiglager'; 9 | $lang['toolbar_inserted_markup'] = 'Tekst i wikiformat laget fra verktøylinjen'; 10 | -------------------------------------------------------------------------------- /lang/pt-br/lang.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | $lang['encoding'] = 'utf-8'; 11 | $lang['direction'] = 'ltr'; 12 | $lang['doesntexist'] = 'este domínio não existe: '; 13 | $lang['subcats'] = 'Subdomínios:'; 14 | $lang['pagesinthiscat'] = 'Páginas neste domínio:'; 15 | $lang['continued'] = ' cont.'; 16 | $lang['nopages'] = 'Nenhuma página neste domínio.'; 17 | $lang['nosubns'] = 'Nenhum subdomínio.'; 18 | $lang['sidebarOrNs'] = 'Com a opção -sidebar, você não pode especificar um domínio. Encontrado:'; 19 | -------------------------------------------------------------------------------- /lang/pt-br/settings.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | $lang['cache'] = 'Desabilitar o cache'; 11 | $lang['default_picture'] = 'Defina sua imagem padrão'; 12 | $lang['toolbar_inserted_markup'] = 'Marcação inserida na barra de ferramentas'; 13 | $lang['custom_title_allow_list_metadata'] = 'Lista de metadados permitidos para a opção -customTitle'; 14 | $lang['global_exclude'] = 'Lista de exclusão global para páginas e domínios'; 15 | -------------------------------------------------------------------------------- /lang/ru/lang.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Alexey Markov 8 | */ 9 | $lang['encoding'] = 'utf-8'; 10 | $lang['direction'] = 'ltr'; 11 | $lang['doesntexist'] = 'Этого пространства имён не существует:'; 12 | $lang['subcats'] = 'Подпространства имён:'; 13 | $lang['pagesinthiscat'] = 'Страницы в этом пространстве имён:'; 14 | $lang['continued'] = ' (продолжение)'; 15 | $lang['nopages'] = 'Нет страниц в этом пространстве имён.'; 16 | $lang['nosubns'] = 'Нет подпространств имён.'; 17 | $lang['sidebarOrNs'] = 'При использовании опции -sidebar нельзя указывать какое- 18 | либо пространство имён. Найдено: '; 19 | -------------------------------------------------------------------------------- /lang/ru/settings.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Vyacheslav Strenadko 8 | * @author Konstantin Bobovskiy 9 | */ 10 | $lang['cache'] = 'Отключить кэширование'; 11 | $lang['default_picture'] = 'Укажите своё изображение по умолчанию'; 12 | $lang['toolbar_inserted_markup'] = 'Разметка для вставки через кнопку на панели инструментов страницы'; 13 | $lang['custom_title_allow_list_metadata'] = 'Метаданные списка разрешённых настраиваемых заголовков'; 14 | $lang['global_exclude'] = 'Глобальный список исключений для страниц и пространств имён'; 15 | -------------------------------------------------------------------------------- /lang/sv/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'följande namnrymd finns ej:'; 11 | $lang['subcats'] = 'Undernamnrymder:'; 12 | $lang['pagesinthiscat'] = 'Sidor i denna namnrymd:'; 13 | $lang['continued'] = ' forts.'; 14 | $lang['nopages'] = 'Inga sidor i denna namnrymd.'; 15 | $lang['nosubns'] = 'Inga undernamnrymder.'; 16 | -------------------------------------------------------------------------------- /lang/sv/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = 'Avaktivera cache'; 9 | $lang['toolbar_inserted_markup'] = 'Märkspråk införd i verktygsfältet'; 10 | -------------------------------------------------------------------------------- /lang/uk/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['doesntexist'] = 'цього простору назв немає:'; 9 | $lang['subcats'] = 'Вкладені простори назв'; 10 | $lang['pagesinthiscat'] = 'Сторінок в просторі назв'; 11 | $lang['continued'] = 'кільк.'; 12 | $lang['nopages'] = 'Простір назв пустий'; 13 | $lang['nosubns'] = 'Відсутні вкладені простори назв'; 14 | -------------------------------------------------------------------------------- /lang/uk/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = 'Заборонити кешування'; 9 | $lang['toolbar_inserted_markup'] = 'Розмітка вставлена з Панелі інструментів (toolbar)'; 10 | -------------------------------------------------------------------------------- /lang/vi/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['encoding'] = 'utf-8'; 9 | $lang['direction'] = 'ltr'; 10 | $lang['doesntexist'] = 'không gian tên namespace này không tồn tại:'; 11 | $lang['subcats'] = 'Không gian tên con Subnamespace:'; 12 | $lang['pagesinthiscat'] = 'Các trang trong không gian tên namespace này:'; 13 | $lang['continued'] = 'cont.'; 14 | $lang['nopages'] = 'Không có trang nào trong không gian tên namespace này.'; 15 | $lang['nosubns'] = 'Không có không gian tên con subnamespace.'; 16 | -------------------------------------------------------------------------------- /lang/vi/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = 'Tắt bộ đệm cache'; 9 | $lang['default_picture'] = 'Đặt hình ảnh mặc định của bạn'; 10 | $lang['toolbar_inserted_markup'] = 'Đánh dấu đã thêm thanh công cụ '; 11 | $lang['custom_title_allow_list_metadata'] = 'Tiêu đề tùy chỉnh cho phép danh sách siêu dữ liệu metadata'; 12 | $lang['global_exclude'] = 'Danh sách loại trừ toàn cầu cho các trang và không gian tên namespace'; 13 | -------------------------------------------------------------------------------- /lang/zh/lang.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['doesntexist'] = '当前命名空间不存在'; 9 | $lang['subcats'] = '子命名空间'; 10 | $lang['pagesinthiscat'] = '此命名空间页数'; 11 | $lang['nopages'] = '此命名空间无页数'; 12 | $lang['nosubns'] = '无子命名空间'; 13 | $lang['sidebarOrNs'] = '-边栏选项下你不可以指定一个命名空间. 见:'; 14 | -------------------------------------------------------------------------------- /lang/zh/settings.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | $lang['cache'] = '取消缓存'; 9 | $lang['default_picture'] = '设置你的默认图片'; 10 | $lang['toolbar_inserted_markup'] = '工具栏插入标志'; 11 | $lang['custom_title_allow_list_metadata'] = '自定义标题允许元数据列'; 12 | $lang['global_exclude'] = '页数与命名空间的全局排除列表'; 13 | -------------------------------------------------------------------------------- /namespaceFinder.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | if(!defined('DOKU_INC')) die(); 9 | 10 | class namespaceFinder { 11 | private $wantedNs; 12 | private $isSafe; 13 | 14 | /** 15 | * Resolves the namespace on construction 16 | * 17 | * @param string $path the namespace link 18 | */ 19 | function __construct($path){ 20 | $this->wantedNs = $this->computeWantedNs($path); 21 | $this->sanitizeNs(); 22 | } 23 | 24 | private function computeWantedNs($wantedNS){ 25 | global $ID; 26 | 27 | // Convert all other separators to colons 28 | // Nb: slashes should be accepted as separator too as they can be a legit separator when the DW conf "useslash" is on. (and anyway slashes are not allowed in page name 29 | // (see https://www.dokuwiki.org/pagename ) so it's the only correct way to deal with it. 30 | // But we don't need to str_replace it because we don't go through "cleanID" (which would handle it when the conf is off) and because we never remove nor escape the slashes 31 | // before they are converted to a FS path 32 | $wantedNS = str_replace(';', ':', $wantedNS); // accepted by DW as namespace separator according to https://www.dokuwiki.org/pagename 33 | 34 | $result = ''; 35 | if($wantedNS == '') { 36 | $wantedNS = $this->getCurrentNamespace(); 37 | } 38 | if( $this->isRelativePath($wantedNS) ) { 39 | $result = getNS($ID); 40 | // normalize initial dots ( ..:..abc -> ..:..:abc ) 41 | $wantedNS = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $wantedNS); 42 | } elseif ( $this->isPageRelativePath($wantedNS) ) { 43 | $result = $ID; 44 | $wantedNS = substr($wantedNS, 1); 45 | } 46 | $result .= ':'.$wantedNS.':'; 47 | return $result; 48 | } 49 | 50 | private function getCurrentNamespace(){ 51 | return '.'; 52 | } 53 | 54 | private function isRelativePath($path){ 55 | return $path[0] == '.'; 56 | } 57 | 58 | private function isPageRelativePath($path){ 59 | return $path[0] == '~'; 60 | } 61 | 62 | /** 63 | * Get rid of '..'. 64 | * Therefore, provides a ns which passes the cleanid() function, 65 | */ 66 | private function sanitizeNs(){ 67 | $ns = explode(':', $this->wantedNs); 68 | 69 | for($i = 0; $i < count($ns); $i++) { 70 | if($ns[$i] === '' || $ns[$i] === '.') { 71 | array_splice($ns, $i, 1); 72 | $i--; 73 | } else if($ns[$i] == '..') { 74 | if($i == 0) { 75 | //the first can't be '..', to stay inside 'data/pages' 76 | break; 77 | } else { 78 | //simplify the path, getting rid of 'ns:..' 79 | array_splice($ns, $i - 1, 2); 80 | $i -= 2; 81 | } 82 | } 83 | } 84 | 85 | $this->isSafe = (count($ns) == 0 || $ns[0] != '..'); 86 | $this->wantedNs = implode(':', $ns); 87 | } 88 | 89 | function getWantedNs(){ 90 | return $this->wantedNs; 91 | } 92 | 93 | function isNsSafe(){ 94 | return $this->isSafe; 95 | } 96 | 97 | function getWantedDirectory(){ 98 | return $this->namespaceToDirectory($this->wantedNs); 99 | } 100 | 101 | static function namespaceToDirectory($ns){ 102 | return utf8_encodeFN(str_replace(':', '/', $ns)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /optionParser.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | use dokuwiki\Utf8\PhpString; 10 | 11 | if(!defined('DOKU_INC')) die(); 12 | 13 | class optionParser { 14 | 15 | static function checkRegEx(&$match, $pattern, &$arrayAffected) { 16 | optionParser::preg_match_all_wrapper($pattern, $match, $found); 17 | foreach($found as $regex) { 18 | $arrayAffected[] = $regex[1]; 19 | $match = optionParser::_removeFromMatch($regex[0], $match); 20 | } 21 | } 22 | 23 | /** 24 | * Check if a given option has been given, and remove it from the initial string 25 | * 26 | * @param string $match The string match by the plugin 27 | * @param string $pattern The pattern which activate the option 28 | * @param mixed $varAffected The variable which will memorise the option 29 | * @param mixed $valIfFound the value affected to the previous variable if the option is found 30 | */ 31 | static function checkOption(&$match, $pattern, &$varAffected, $valIfFound) { 32 | if(optionParser::preg_match_wrapper($pattern, $match, $found)) { 33 | $varAffected = $valIfFound; 34 | $match = optionParser::_removeFromMatch($found[0], $match); 35 | } 36 | } 37 | 38 | static function checkRecurse(&$match, &$varAffected){ 39 | if(optionParser::preg_match_wrapper('r *=? *\"?([[:digit:]]*)\"?', $match, $found)) { 40 | if($found[1] != '') { 41 | $varAffected = (int) $found[1]; 42 | } else { 43 | $varAffected = 0; //no limit 44 | } 45 | $match = optionParser::_removeFromMatch($found[0], $match); 46 | } 47 | } 48 | 49 | static function checkNbColumns(&$match, &$varAffected){ 50 | if(optionParser::preg_match_wrapper("nb?Cols? *=? *\"?([[:digit:]]*)\"?", $match, $found)) { 51 | if($found[1] != '') { 52 | $varAffected = max((int) $found[1], 1); 53 | } 54 | $match = optionParser::_removeFromMatch($found[0], $match); 55 | } 56 | } 57 | 58 | static function checkNbItemsMax(&$match, &$varAffected){ 59 | if(optionParser::preg_match_wrapper("nb?Items?Max *=? *\"?([[:digit:]]*)\"?", $match, $found)) { 60 | if($found[1] != '') { 61 | $varAffected = max((int) $found[1], 1); 62 | } 63 | $match = optionParser::_removeFromMatch($found[0], $match); 64 | } 65 | } 66 | 67 | static function checkAnchorName(&$match, &$varAffected){ 68 | if(optionParser::preg_match_wrapper("anchorName *=? *\"?([[:alnum:]]+)\"?", $match, $found)) { 69 | $varAffected = $found[1]; 70 | $match = optionParser::_removeFromMatch($found[0], $match); 71 | } 72 | } 73 | 74 | static function checkSimpleStringArgument(&$match, &$varAffected, $plugin, $argumentName){ 75 | if(optionParser::preg_match_wrapper($argumentName . " *= *\"([^\"]*)\"", $match, $found)) { 76 | $varAffected = $found[1]; 77 | $match = optionParser::_removeFromMatch($found[0], $match); 78 | } else { 79 | $varAffected = null; 80 | } 81 | } 82 | 83 | static function checkDictOrder(&$match, &$varAffected, $plugin){ 84 | if(optionParser::preg_match_wrapper("dict(?:ionary)?Order *= *\"([^\"]*)\"", $match, $found)) { 85 | $varAffected = $found[1]; 86 | $match = optionParser::_removeFromMatch($found[0], $match); 87 | } else { 88 | $varAffected = null; 89 | } 90 | } 91 | 92 | static function checkExclude(&$match, &$excludedPages, &$excludedNs, &$excludeSelfPage){ 93 | //--Looking if the syntax -exclude[item1 item2] has been used 94 | if(optionParser::preg_match_wrapper("exclude:\[(.*)\]", $match, $found)) { 95 | $match = optionParser::_removeFromMatch($found[0], $match); 96 | self::_addListOfItemsToExclude(explode(' ', $found[1]), $excludedPages, $excludedNs); 97 | } 98 | 99 | //--Checking if specified subnamespaces have to be excluded 100 | optionParser::preg_match_all_wrapper("exclude:([^[ <>]*):", $match, $found); 101 | foreach($found as $subns) { 102 | $excludedNs[] = $subns[1]; 103 | $match = optionParser::_removeFromMatch($subns[0], $match); 104 | } 105 | 106 | //--Checking if specified pages have to be excluded 107 | optionParser::preg_match_all_wrapper("exclude:([^[ <>]*)", $match, $found); 108 | foreach($found as $page) { 109 | $excludedPages[] = $page[1]; 110 | $match = optionParser::_removeFromMatch($page[0], $match); 111 | } 112 | 113 | //--Looking if the current page has to be excluded 114 | if(optionParser::preg_match_wrapper("exclude", $match, $found)) { 115 | $excludeSelfPage = true; 116 | $match = optionParser::_removeFromMatch($found[0], $match); 117 | } 118 | } 119 | 120 | static function checkGlobalExclude($globalExclude, &$excludedPages, &$excludedNs) { 121 | if(!empty($globalExclude)) { 122 | self::_addListOfItemsToExclude(explode(',', $globalExclude), $excludedPages, $excludedNs); 123 | } 124 | } 125 | 126 | private static function _addListOfItemsToExclude($excludeList, &$excludedPages, &$excludedNs) { 127 | foreach($excludeList as $exclude) { 128 | $exclude = trim($exclude); 129 | if ($exclude === "") { 130 | return; 131 | } 132 | if($exclude[-1] === ':') { 133 | $excludedNs[] = PhpString::substr($exclude, 0, -1); 134 | } else { 135 | $excludedPages[] = $exclude; 136 | } 137 | } 138 | } 139 | 140 | static function checkActualTitle(&$match, &$varAffected){ 141 | $foundOption = false; 142 | if ( optionParser::preg_match_wrapper("actualTitle *= *([[:digit:]])", $match, $found) ){ 143 | $varAffected = $found[1]; 144 | $foundOption = true; 145 | } else if ( optionParser::preg_match_wrapper("actualTitle", $match, $found) ){ 146 | $varAffected = 2; 147 | $foundOption = true; 148 | } 149 | 150 | if ($foundOption) { 151 | $match = optionParser::_removeFromMatch($found[0], $match); 152 | } 153 | } 154 | 155 | static private function preg_match_wrapper($pattern, $subject, &$matches){ 156 | return preg_match('/\s-' . $pattern . '/i', $subject, $matches); 157 | } 158 | 159 | static private function preg_match_all_wrapper($pattern, $subject, &$matches){ 160 | return preg_match_all('/\s-' . $pattern . '/i', $subject, $matches, PREG_SET_ORDER); 161 | } 162 | 163 | static private function _removeFromMatch($matched, $match){ 164 | $matched = trim($matched); // to handle the case of the option "-r" which already matches an extra whitespace 165 | // Matched option including any leading and at least one trailing whitespace 166 | $regex = '/\s*' . preg_quote($matched, '/') . '\s+/'; 167 | return preg_replace($regex, ' ', $match); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /plugin.info.txt: -------------------------------------------------------------------------------- 1 | base nspages 2 | author Guillaume Turri 3 | email guillaume.turri@gmail.com 4 | date 2024-11-22 5 | name nspages plugin 6 | desc Presents a toc of the selected namespace using 7 | url http://www.dokuwiki.org/plugin:nspages 8 | -------------------------------------------------------------------------------- /printers/printer.php: -------------------------------------------------------------------------------- 1 | tag in a same page 25 | static private $builtSectionIds = array(); 26 | 27 | function __construct($plugin, $mode, $renderer, $data){ 28 | $this->plugin = $plugin; 29 | $this->renderer =& $renderer; 30 | $this->mode = $mode; 31 | $this->pos = $data['pos']; 32 | $this->natOrder = $data['natOrder']; 33 | $this->actualTitleLevel = $data['actualTitleLevel']; 34 | $this->nbItemsMax = $data['nbItemsMax']; 35 | $this->dictOrder = $data['dictOrder']; 36 | $this->_displayModificationDate = $data['displayModificationDate'] 37 | || $data['modificationDateOnPictures']; // This is a deprecated option. We should kill it after checking no users are still using it 38 | $this->_sorter = $this->_getSorter($data['reverse']); 39 | $this->includeItemsInTOC = $data['includeItemsInTOC'] && $mode === 'xhtml'; 40 | } 41 | 42 | function printTOC($tab, $type, $text, $hideno){ 43 | $this->_printHeader($tab, $type, $text, $hideno); 44 | 45 | if(empty($tab)) { 46 | return; 47 | } 48 | 49 | $this->_print($tab, $type); 50 | } 51 | 52 | abstract function _print($tab, $type); 53 | 54 | function printUnusableNamespace($wantedNS){ 55 | $this->printError($this->plugin->getLang('doesntexist').$wantedNS); 56 | } 57 | 58 | function printErrorSidebarDoestAcceptNamespace($wantedNS){ 59 | $this->printError($this->plugin->getLang('sidebarOrNs').$wantedNS); 60 | } 61 | 62 | private function printError($errorMessage){ 63 | $this->renderer->section_open(1); 64 | $this->renderer->cdata($errorMessage); 65 | $this->renderer->section_close(); 66 | } 67 | 68 | private function _printHeader(&$tab, $type, $text, $hideno) { 69 | if(empty($tab) && $hideno) return; 70 | 71 | $this->_sorter->sort($tab); 72 | $this->_keepOnlyNMaxItems($tab); 73 | 74 | if($text != '') { 75 | if($this->actualTitleLevel){ 76 | $this->renderer->header($text, $this->actualTitleLevel, $this->pos); 77 | } else if($this->mode == 'xhtml') { 78 | $this->renderer->doc .= '

'; 79 | $this->renderer->cdata($text); 80 | $this->renderer->doc .= '

'; 81 | } else { 82 | $this->renderer->linebreak(); 83 | $this->renderer->p_open(); 84 | $this->renderer->cdata($text); 85 | $this->renderer->p_close(); 86 | } 87 | } 88 | 89 | if(empty($tab)) { 90 | $this->renderer->p_open(); 91 | $this->renderer->cdata($this->plugin->getLang(($type == 'page') ? 'nopages' : 'nosubns')); 92 | $this->renderer->p_close(); 93 | } 94 | } 95 | 96 | private function _getSorter($reverse) { 97 | if ( $this->natOrder ){ 98 | return new nspages_naturalOrder_sorter($reverse); 99 | } else if ($this->dictOrder) { 100 | return new nspages_dictOrder_sorter($reverse, $this->dictOrder); 101 | } else { 102 | return new nspages_default_sorter($reverse); 103 | } 104 | } 105 | 106 | private function _keepOnlyNMaxItems(&$tab){ 107 | if ($this->nbItemsMax){ 108 | $tab = array_slice($tab, 0, $this->nbItemsMax); 109 | } 110 | } 111 | 112 | /** 113 | * @param Array $item Represents the file 114 | * @param bool $node true when a node; false when a leaf 115 | */ 116 | protected function _printElement($item, $level=1) { 117 | $this->_printElementOpen($item, $level); 118 | $this->_printElementContent($item, $level); 119 | $this->_printElementClose(); 120 | } 121 | 122 | protected function _printElementOpen($item, $level) { 123 | if($item == null || $item['type'] !== 'd') { 124 | $this->renderer->listitem_open($level, false); 125 | } else { //Case of a subnamespace 126 | $this->renderer->listitem_open($level, true); 127 | } 128 | } 129 | 130 | protected function _printElementContent($item, $level=1) { 131 | $this->renderer->listcontent_open(); 132 | $this->_printElementLink($item, $level); 133 | $this->renderer->listcontent_close(); 134 | } 135 | 136 | protected function _printElementLink($item, $level=1) { 137 | $linkText = ""; 138 | if ($this->_displayModificationDate) { 139 | $linkText = '[' . date('Y-m-d', $item["mtime"]) . '] - '; 140 | } 141 | $linkText .= $item['nameToDisplay']; 142 | if ($this->includeItemsInTOC){ 143 | $anchorId = $this->buildAnchorId($item); 144 | $this->renderer->doc .= ''; 145 | $this->renderer->toc_additem($anchorId, $linkText, $this->renderer->getLastLevel() + $level); 146 | } 147 | $this->renderer->internallink(':'.$item['id'], $linkText); 148 | if ($this->includeItemsInTOC){ 149 | $this->renderer->doc .= ""; 150 | } 151 | } 152 | 153 | protected function buildAnchorId($item){ 154 | // Prefix with "nspages_" to avoid collisions with headers 155 | return "nspages_" . sectionID($item['id'], self::$builtSectionIds); 156 | } 157 | 158 | protected function _printElementClose() { 159 | $this->renderer->listitem_close(); 160 | } 161 | 162 | function printBeginning(){ 163 | if($this->mode == 'xhtml') { 164 | $this->renderer->doc .= '
'; 165 | } 166 | } 167 | 168 | function printEnd(){ 169 | //this is needed to make sure everything after the plugin is written below the output 170 | if($this->mode == 'xhtml') { 171 | $this->renderer->doc .= '
'; 172 | $this->renderer->doc .= '
'; 173 | } else { 174 | $this->renderer->linebreak(); 175 | } 176 | } 177 | 178 | function printTransition(){ } 179 | } 180 | -------------------------------------------------------------------------------- /printers/printerLineBreak.php: -------------------------------------------------------------------------------- 1 | renderer->linebreak(); 19 | } 20 | $this->_printElementLink($item); 21 | $firstItem = false; 22 | } 23 | } 24 | 25 | function printTransition(){ 26 | $this->renderer->cdata(', '); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /printers/printerNice.php: -------------------------------------------------------------------------------- 1 | mode !== 'xhtml' ){ 21 | throw Exception('nspages_printerNice can only work in xhtml mode'); 22 | } 23 | $this->nbCols = $this->_computeActualNbCols($nbCols); 24 | $this->anchorName = $anchorName; 25 | } 26 | 27 | private function _computeActualNbCols($nbCols){ 28 | $nbCols = (int) $nbCols; 29 | if(!isset($nbCols) || is_null($nbCols) || $nbCols < 1) { 30 | $nbCols = 3; 31 | } 32 | return $nbCols; 33 | } 34 | 35 | function _print($tab, $type) { 36 | $nbItemsPrinted = 0; 37 | 38 | $nbItemPerColumns = $this->_computeNbItemPerColumns(sizeof($tab)); 39 | $actualNbCols = count($nbItemPerColumns); 40 | $helper = new rendererXhtmlHelper($this->renderer, $actualNbCols, $this->plugin, $this->anchorName); 41 | 42 | $helper->openColumn(); 43 | $firstCharOfLastAddedPage = $this->_firstChar($tab[0]); 44 | 45 | $helper->printHeaderChar($firstCharOfLastAddedPage); 46 | $helper->openListOfItems(); 47 | 48 | $idxCol = 0; 49 | foreach($tab as $item) { 50 | //change to the next column if necessary 51 | if($nbItemsPrinted == $nbItemPerColumns[$idxCol]) { 52 | $idxCol++; 53 | $helper->closeListOfItems(); 54 | $helper->closeColumn(); 55 | $helper->openColumn(); 56 | 57 | $newLetter = $this->_firstChar($item); 58 | if($newLetter != $firstCharOfLastAddedPage) { 59 | $firstCharOfLastAddedPage = $newLetter; 60 | $helper->printHeaderChar($firstCharOfLastAddedPage); 61 | } else { 62 | $helper->printHeaderChar($firstCharOfLastAddedPage, true); 63 | } 64 | $helper->openListOfItems(); 65 | } 66 | 67 | $newLetter = $this->_firstChar($item); 68 | if($newLetter != $firstCharOfLastAddedPage) { 69 | $firstCharOfLastAddedPage = $newLetter; 70 | $helper->closeListOfItems(); 71 | $helper->printHeaderChar($firstCharOfLastAddedPage); 72 | $helper->openListOfItems(); 73 | } 74 | 75 | $this->_printElement($item); 76 | $nbItemsPrinted++; 77 | } 78 | $helper->closeListOfItems(); 79 | $helper->closeColumn(); 80 | } 81 | 82 | private function _firstChar($item) { 83 | $return_char = PhpString::strtoupper(PhpString::substr($item['sort'], 0, 1)); 84 | $uniord_char = $this->_uniord($return_char); 85 | 86 | // korean support. See #111 for more context 87 | if ($uniord_char > 44031 && $uniord_char < 55204) { 88 | $return_char = ['ㄱ','ㄱ','ㄴ','ㄷ','ㄷ','ㄹ','ㅁ','ㅂ','ㅂ','ㅅ','ㅅ','ㅇ','ㅈ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'][($uniord_char-44032)/588]; 89 | } 90 | 91 | return $return_char; 92 | } 93 | 94 | /** 95 | * This code is from: 96 | * https://stackoverflow.com/questions/9361303/ 97 | */ 98 | private function _uniord($c) { 99 | if (ord($c[0]) >=0 && ord($c[0]) <= 127) 100 | return ord($c[0]); 101 | if (ord($c[0]) >= 192 && ord($c[0]) <= 223) 102 | return (ord($c[0])-192)*64 + (ord($c[1])-128); 103 | if (ord($c[0]) >= 224 && ord($c[0]) <= 239) 104 | return (ord($c[0])-224)*4096 + (ord($c[1])-128)*64 + (ord($c[2])-128); 105 | if (ord($c[0]) >= 240 && ord($c[0]) <= 247) 106 | return (ord($c[0])-240)*262144 + (ord($c[1])-128)*4096 + (ord($c[2])-128)*64 + (ord($c[3])-128); 107 | if (ord($c[0]) >= 248 && ord($c[0]) <= 251) 108 | return (ord($c[0])-248)*16777216 + (ord($c[1])-128)*262144 + (ord($c[2])-128)*4096 + (ord($c[3])-128)*64 + (ord($c[4])-128); 109 | if (ord($c[0]) >= 252 && ord($c[0]) <= 253) 110 | return (ord($c[0])-252)*1073741824 + (ord($c[1])-128)*16777216 + (ord($c[2])-128)*262144 + (ord($c[3])-128)*4096 + (ord($c[4])-128)*64 + (ord($c[5])-128); 111 | if (ord($c[0]) >= 254 && ord($c[0]) <= 255) 112 | return false; 113 | return false; 114 | } 115 | 116 | /** 117 | * Compute the number of element to display per column 118 | * When $nbItems / $nbCols isn't an int, we make sure, for aesthetic reasons, 119 | * that the first are the ones which have the more items 120 | * Moreover, if we don't have enought items to display, we may choose to display less than the number of columns wanted 121 | * 122 | * @param int $nbItems The total number of items to display 123 | * @return an array which contains $nbCols int. 124 | */ 125 | private function _computeNbItemPerColumns($nbItems) { 126 | $result = array(); 127 | 128 | if($nbItems < $this->nbCols) { 129 | for($idx = 0; $idx < $nbItems; $idx++) { 130 | $result[] = $idx + 1; 131 | } 132 | return $result; 133 | } 134 | 135 | $collength = $nbItems / $this->nbCols; 136 | $nbItemPerCol = array(); 137 | for($idx = 0; $idx < $this->nbCols; $idx++) { 138 | $nbItemPerCol[] = ceil(($idx + 1) * $collength) - ceil($idx * $collength); 139 | } 140 | rsort($nbItemPerCol); 141 | 142 | $result[] = $nbItemPerCol[0]; 143 | for($idx = 1; $idx < $this->nbCols; $idx++) { 144 | $result[] = end($result) + $nbItemPerCol[$idx]; 145 | } 146 | 147 | return $result; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /printers/printerOneLine.php: -------------------------------------------------------------------------------- 1 | renderer->cdata($sep); 20 | $this->_printElementLink($item); 21 | $sep = ', '; 22 | } 23 | } 24 | 25 | function printTransition(){ 26 | $this->renderer->cdata(', '); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /printers/printerPictures.php: -------------------------------------------------------------------------------- 1 | 350, 'h' => 220); 13 | private $_defaultPicture; 14 | 15 | function __construct($plugin, $mode, $renderer, $data){ 16 | parent::__construct($plugin, $mode, $renderer, $data); 17 | $this->_defaultPicture = $data['defaultPicture']; 18 | } 19 | 20 | function _print($tab, $type) { 21 | $this->renderer->doc .= ''; 45 | } 46 | 47 | private function _getFirstImage($pageId){ 48 | $meta = p_get_metadata($pageId); 49 | $picture = $meta['relation']['firstimage']; 50 | if ( $picture != "" ){ 51 | return ml($picture, self::$_dims, true); 52 | } else { 53 | if ( $this->_defaultPicture == '' ){ 54 | return "lib/tpl/dokuwiki/images/logo.png"; 55 | } else { 56 | return ml($this->_defaultPicture, self::$_dims, true); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /printers/printerSimpleList.php: -------------------------------------------------------------------------------- 1 | useNumberedList = $useNumberedList; 17 | } 18 | 19 | function _print($tab, $type) { 20 | $this->_openList(); 21 | $this->_printItems($tab); 22 | $this->_closeList(); 23 | } 24 | 25 | private function _openList() { 26 | if ( $this->useNumberedList ){ 27 | $this->renderer->listo_open(); 28 | } else { 29 | $this->renderer->listu_open(); 30 | } 31 | } 32 | 33 | private function _printItems($tab){ 34 | foreach($tab as $item) { 35 | $this->_printElement($item); 36 | } 37 | } 38 | 39 | private function _closeList() { 40 | if ( $this->useNumberedList ){ 41 | $this->renderer->listo_close(); 42 | } else { 43 | $this->renderer->listu_close(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /printers/printerTree.php: -------------------------------------------------------------------------------- 1 | rootNS = $data['wantedNS'] . ':'; 17 | } 18 | 19 | function _print($tab, $type) { 20 | $tree = $this->_groupByNs($tab); 21 | $trimmedTree = $this->_getTrimmedTree($tree); 22 | $orderedTree = $this->_orderTree($trimmedTree); 23 | $this->_printTree($orderedTree); 24 | } 25 | 26 | /** 27 | * We received the nodes all ordered together, but building the tree has probably 28 | * lost the order for namespaces, we hence need to sort again each node 29 | */ 30 | function _orderTree($tree) { 31 | // We only need to sort "children". We don't need to sort "pages" because with the current 32 | // workflow of the plugin nodes are provided already sorted to _print, and the way we 33 | // build the tree preserves the order of the pages. 34 | // An optimization could be to disable the preliminary sort and to instead sort pages here. 35 | // That could save some CPU cycles because instead of sorting a big list we would sort 36 | // several smaller ones. However it would require 37 | // - a regression test which assert on the order of the pages when a flag is passed to 38 | // have a custom sort (eg: "-h1") to ensure we don't have the correct order just because 39 | // the DW search API returned sorted results based on the id of the pages 40 | // - benchmarking (because it could be detrimental if usort has a constant overhead which 41 | // would make several small sort more costly than a single one bigger) 42 | $this->_sorter->sort($tree->children); 43 | 44 | foreach($tree->children as $subTree){ 45 | if (is_object($subTree)) { 46 | $this->_orderTree($subTree); 47 | } 48 | } 49 | return $tree; 50 | } 51 | 52 | private function _groupByNs($tab) { 53 | $tree = new NspagesTreeNsNode(':'); 54 | foreach($tab as $item){ 55 | $this->_fillTree($tree, $this->_getNS($item), $item, '', ':'); 56 | } 57 | return $tree; 58 | } 59 | 60 | /** 61 | * Get rid of the "trunk" of the tree. ie: remove the first "empty" nodes. It prevents printing 62 | * something like 63 | * - A 64 | * - B 65 | * - C 66 | * - page1 67 | * - page2 68 | * - page3 69 | * when the ns the user asked for is actully ns C 70 | */ 71 | private function _getTrimmedTree($tree){ 72 | if ($tree->id === $this->rootNS){ 73 | return $tree; 74 | } else { 75 | if (is_null($tree->children)) { 76 | // This case should never happen. But I handle it neverthelss because if I'm wrong 77 | // then the recursion will never end 78 | return $tree; 79 | } 80 | $firstAndOnlyChild = reset($tree->children); 81 | return $this->_getTrimmedTree($firstAndOnlyChild); 82 | } 83 | } 84 | 85 | private function _getNS($item) { 86 | if($item['type'] === 'd'){ 87 | // If $item is itself a namespace then: 88 | // - its 'id' will look like either: 89 | // 1. 'a:b:c:' if the ns has no main page 90 | // 2. 'a:b:c:start' or 'a:b:c:c' (if this page exists) 91 | // 3. 'a:b:c' (case where there is a page a:b:c and no page a:b:c:start, see bug #120) 92 | // - its 'ns' will look like 'a:b' 93 | // What we want is array ['a', 'b', 'c'] 94 | 95 | // For a page at the root of the repo: 96 | // - the 'id' will look like either 97 | // 4. 'a:start' in most cases 98 | // 5. 'a' (case where the is a page 'a' and no page 'a:start', see bug #120) 99 | // - the 'ns' will be FALSE 100 | 101 | $lastChar = substr($item['id'], -1); 102 | $IdSplit = explode(':', $item['id']); 103 | 104 | if ($item['ns'] !== false){ 105 | if ($lastChar === ':' // case 1 106 | || count(explode(':', $item['ns'])) === count($IdSplit) -2){ // case 2 107 | array_pop($IdSplit); 108 | } else { // case 3 (nothing to do here) 109 | } 110 | } else { 111 | if ($this->str_contains($item['id'], ':')){ // case 4 112 | array_pop($IdSplit); 113 | } else { // case 5 (nothing to do here) 114 | } 115 | } 116 | 117 | return $IdSplit; 118 | } else { 119 | // It $item is a page then: 120 | // - its 'id' will look like 'a:b:page' 121 | // - its 'ns' will look like 'a:b' 122 | // What we want is array ['a', 'b'] 123 | if ($item['ns'] === false) { 124 | // Special case of the pages at the root of the wiki: for them "ns" is set to boolean FALSE 125 | return array(); 126 | } else { 127 | return explode(':', $item['ns']); 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * This is similar to https://www.php.net/manual/en/function.str-contains.php, but the PHP str_contains 134 | * method is available only from PHP 8 so for now we re-implement this feature 135 | */ 136 | private function str_contains(string $haystack, string $needle){ 137 | return strpos($haystack, $needle) !== false; 138 | } 139 | 140 | private function _fillTree($tree, $keys, $item, $parentId, $myNs) { 141 | if (empty($keys)){ // We've reach the end of the journey. We register the data of $item 142 | if($item['type'] === 'd') { 143 | $tree->self = $item; 144 | } else { 145 | if ($myNs == $item['id']) { 146 | $tree->self = $item; 147 | } else { 148 | if (!isset($tree->children[$item['id']])) { 149 | if ('d' !== $item['type']) { 150 | $tree->children[$item['id']] = new NspagesTreeNsNode($item['id']); 151 | $tree->children[$item['id']]->self = $item; 152 | } else { 153 | $tree->children[$item['id']] = $item; 154 | } 155 | } else { 156 | $tree->children[$item['id']]->self = $item; 157 | } 158 | } 159 | } 160 | } else { // We're not at the place of $item in the tree yet, we continue to go down 161 | $key = $keys[0]; 162 | $currentId = $parentId . $key . ':'; 163 | $nsKey = $parentId . $key; 164 | if (!array_key_exists($nsKey, $tree->children)){ 165 | $node = new NspagesTreeNsNode($currentId); 166 | $tree->children[$nsKey] = $node; 167 | } 168 | array_shift($keys); 169 | $this->_fillTree($tree->children[$nsKey], $keys, $item, $currentId, $item['ns']); 170 | } 171 | } 172 | 173 | private function _printTree($tree) { 174 | $this->renderer->listu_open(); 175 | 176 | foreach($tree->children as $subTree){ 177 | if (is_object($subTree)) { 178 | $this->_printSubTree($subTree, 1); 179 | } else { 180 | $this->_printElement($subTree, 1); 181 | } 182 | } 183 | 184 | $this->renderer->listu_close(); 185 | } 186 | 187 | private function _printSubTree($tree, $level) { 188 | $this->_printElementOpen($tree->self, $level); 189 | if ( !is_null($tree->self) ){ 190 | $this->_printElementContent($tree->self, $level); 191 | } else { 192 | $this->renderer->doc .= '
' . $tree->id . '
'; 193 | } 194 | 195 | $hasInnerData = !empty($tree->children); 196 | if($hasInnerData){ 197 | $this->renderer->listu_open(); 198 | } 199 | foreach($tree->children as $subTree){ 200 | if (is_object($subTree)) { 201 | $this->_printSubTree($subTree, $level+1); 202 | } else { 203 | $this->_printElement($subTree, $level+1); 204 | } 205 | } 206 | 207 | if($hasInnerData){ 208 | $this->renderer->listu_close(); 209 | } 210 | $this->_printElementClose(); 211 | } 212 | } 213 | 214 | /** 215 | * Represent a namespace and its inner content 216 | */ 217 | class NspagesTreeNsNode implements ArrayAccess { 218 | 219 | /** 220 | * The list of pages and subnamespaces at level n+1 (does not include their own subnamespaces) 221 | */ 222 | public $children = array(); 223 | 224 | /** 225 | * The data about the current namespace iteslf. It may be empty in two cases: 226 | * - when nspages is displaying only pages (because in that case we did not search for ns) 227 | * - when this instance represents the root of the tree (because nspages doesn't display it) 228 | */ 229 | public $self = null; 230 | 231 | /** 232 | * Used to represent the current namespace when we're in a case where we want to display it 233 | * but when $self is empty. 234 | * In practice it is used to represent namespace nodes when we're asked to display pages only 235 | */ 236 | public $id = null; 237 | 238 | function __construct($id){ 239 | $this->id = $id; 240 | } 241 | 242 | /** 243 | * Implement ArrayAccess because instances of this class should be sortable with nspages_sorter 244 | * implementations and that those implementation are performing sorts based on $item["sort"]. 245 | */ 246 | function offsetSet(mixed $offset, mixed $value): void { 247 | throw new BadMethodCallException("Not implemented by design"); 248 | } 249 | function offsetExists(mixed $offset): bool { 250 | return $offset == "sort"; 251 | } 252 | function offsetUnset(mixed $offset): void { 253 | throw new BadMethodCallException("Not implemented by design"); 254 | } 255 | function offsetGet(mixed $offset): mixed { 256 | return is_null($this->self) ? 257 | $this->id : 258 | $this->self["sort"]; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /printers/rendererXhtmlHelper.php: -------------------------------------------------------------------------------- 1 | renderer =& $renderer; 18 | $this->percentWidth = $this->buildWidth($nbCols); 19 | $this->plugin = $plugin; 20 | $this->anchorName = $anchorName; 21 | } 22 | 23 | private function buildWidth($nbCols){ 24 | return (100 / $nbCols) . '%'; 25 | } 26 | 27 | function printHeaderChar($char, $continued = false){ 28 | $text = $char; 29 | if ( $continued ){ 30 | $text .= $this->plugin->getLang('continued'); 31 | } 32 | 33 | $this->renderer->doc .= '
fullAnchor($char, $continued) 35 | . 'class="catpagechars'; 36 | if ( $continued ){ 37 | $this->renderer->doc .= ' continued'; 38 | } 39 | $this->renderer->doc .= '">' . $text . "
\n"; 40 | } 41 | 42 | private function fullAnchor($char, $continued){ 43 | if ( $continued === true || is_null($this->anchorName) ){ 44 | return ''; 45 | } 46 | 47 | return 'id="nspages_' . $this->anchorName . '_' . $char . '" '; 48 | } 49 | 50 | function openColumn(){ 51 | $this->renderer->doc .= "\n".'
'; 52 | } 53 | 54 | function closeColumn(){ 55 | $this->renderer->doc .= "
\n"; 56 | } 57 | 58 | function openListOfItems(){ 59 | $this->renderer->doc .= "
    \n"; 60 | } 61 | 62 | function closeListOfItems(){ 63 | $this->renderer->doc .= '
'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /printers/sorters.php: -------------------------------------------------------------------------------- 1 | reverse = $reverse; 15 | } 16 | 17 | function sort(&$array){ 18 | $this->actualSort($array); 19 | if ($this->reverse) { 20 | $array = array_reverse($array); 21 | } 22 | } 23 | 24 | protected function actualSort(&$array){ 25 | usort($array, array($this, 'comparator')); 26 | } 27 | 28 | abstract function comparator($item1, $item2); 29 | } 30 | 31 | class nspages_default_sorter extends nspages_sorter { 32 | function __construct($reverse){ 33 | parent::__construct($reverse); 34 | } 35 | 36 | function comparator($item1, $item2){ 37 | return strcasecmp($item1['sort'], $item2['sort']); 38 | } 39 | } 40 | 41 | class nspages_naturalOrder_sorter extends nspages_sorter { 42 | function __construct($reverse){ 43 | parent::__construct($reverse); 44 | } 45 | 46 | function comparator($item1, $item2){ 47 | return strnatcasecmp($item1['sort'], $item2['sort']); 48 | } 49 | } 50 | 51 | class nspages_dictOrder_sorter extends nspages_sorter { 52 | private $dictOrder; 53 | 54 | function __construct($reverse, $dictOrder){ 55 | parent::__construct($reverse); 56 | $this->dictOrder = $dictOrder; 57 | } 58 | 59 | function actualSort(&$array){ 60 | $oldLocale=setlocale(LC_ALL, 0); 61 | setlocale(LC_COLLATE, $this->dictOrder); 62 | usort($array, array($this, "comparator")); 63 | setlocale(LC_COLLATE, $oldLocale); 64 | } 65 | 66 | function comparator($item1, $item2){ 67 | return strcoll($item1['sort'], $item2['sort']); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * style.css for Plugin nspages 3 | * 4 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 | * @author Daniel Schranz 6 | * 7 | */ 8 | 9 | /* the headlines (categories and pages in this category) */ 10 | .catpageheadline { 11 | text-decoration: underline; 12 | font-size: x-large; 13 | clear: left; 14 | margin: 0 0 0.7em 0; 15 | } 16 | 17 | /* the letters to indicate the following pages start with a new letter */ 18 | .catpagechars { 19 | font-weight: bold; 20 | } 21 | 22 | .catpagecol { 23 | position: relative; 24 | float: left; 25 | } 26 | 27 | /* to make sure anything after the index will be written beneath */ 28 | .catpageeofidx { 29 | clear: left; 30 | } 31 | 32 | div.catpagecol ul.nspagesul { 33 | margin-bottom: 0; 34 | } 35 | 36 | /* below: all the styles for the usePictures mode */ 37 | div.nspagesPicturesModeMain a:hover { 38 | background-color:#f4ff4f; 39 | } 40 | 41 | div.nspagesPicturesModeMain a { 42 | padding:5px; 43 | color: #ff0000; 44 | background-color:#eee; 45 | font-size: 100%; 46 | width:32%; 47 | max-width: 350px; 48 | height:230px; 49 | float:left; 50 | margin:5px; 51 | } 52 | 53 | div.nspagesPicturesModeImg { 54 | padding:5px; 55 | overflow: hidden; 56 | background-size: cover; 57 | background-position: 50% 50%; 58 | width: 100%; 59 | height: 220px; 60 | float:left; 61 | } 62 | 63 | .nspagesPicturesModeTitle { 64 | background-color:#f4ff4f; 65 | display:block; 66 | overflow: hidden; 67 | font-size: 16px; 68 | font-weight: bold; 69 | text-align: left; 70 | color: #444; 71 | font-family: 'Lato', 'Arial', sans-serif; 72 | text-decoration: none;max-width: 320px; 73 | margin-left: -5px; 74 | margin-top: 165px; 75 | padding-left: 3px; 76 | } 77 | 78 | .nspagesPicturesDate { 79 | overflow: hidden; 80 | font-size: 11px; 81 | font-weight: normal; 82 | text-align: left; 83 | color: #eee; 84 | font-family: 'Lato', 'Arial', sans-serif; 85 | text-decoration: none; 86 | background: black; 87 | padding-right: 10px; 88 | padding-left:5px; 89 | margin-left: -5px; 90 | } 91 | 92 | /* ---------- RTL --------------*/ 93 | 94 | [dir=rtl] div.nspagesPicturesModeMain a:hover { 95 | background-color:#f4ff4f; 96 | } 97 | 98 | [dir=rtl] div.nspagesPicturesModeMain a { 99 | padding:5px; 100 | color: #ff0000; 101 | background-color:#eee; 102 | font-size: 100%; 103 | width:32%; 104 | max-width: 350px; 105 | height:230px; 106 | float:left; 107 | margin:5px; 108 | } 109 | 110 | [dir=rtl] div.nspagesPicturesModeImg { 111 | padding:5px; 112 | overflow: hidden; 113 | background-size: cover; 114 | background-position: 50% 50%; 115 | width: 100%; 116 | height: 220px; 117 | float:left; 118 | } 119 | 120 | [dir=rtl] .nspagesPicturesModeTitle { 121 | background-color:#f4ff4f; 122 | display:block; 123 | overflow: hidden; 124 | font-size: 16px; 125 | font-weight: bold; 126 | text-align: left; 127 | color: #444; 128 | font-family: 'Lato', 'Arial', sans-serif; 129 | text-decoration: none;max-width: 320px; 130 | margin-left: -5px; 131 | margin-top: 165px; 132 | padding-left: 3px; 133 | } 134 | 135 | [dir=rtl] .nspagesPicturesDate { 136 | overflow: hidden; 137 | font-size: 11px; 138 | font-weight: normal; 139 | text-align: left; 140 | color: #eee; 141 | font-family: 'Lato', 'Arial', sans-serif; 142 | text-decoration: none; 143 | background: black; 144 | padding-right: 10px; 145 | padding-left:5px; 146 | margin-left: -5px; 147 | } 148 | -------------------------------------------------------------------------------- /syntax.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Daniel Schranz 8 | * @author Ignacio Bergmann 9 | * @author Andreas Gohr 10 | * @author Ghassem Tofighi 11 | */ 12 | 13 | use dokuwiki\Utf8\PhpString; 14 | 15 | if(!defined('DOKU_INC')) die(); 16 | require_once 'printers/printerLineBreak.php'; 17 | require_once 'printers/printerOneLine.php'; 18 | require_once 'printers/printerSimpleList.php'; 19 | require_once 'printers/printerNice.php'; 20 | require_once 'printers/printerPictures.php'; 21 | require_once 'printers/printerTree.php'; 22 | require_once 'fileHelper/fileHelper.php'; 23 | require_once 'optionParser.php'; 24 | require_once 'namespaceFinder.php'; 25 | 26 | /** 27 | * All DokuWiki plugins to extend the parser/rendering mechanism 28 | * need to inherit from this class 29 | */ 30 | class syntax_plugin_nspages extends DokuWiki_Syntax_Plugin { 31 | function connectTo($aMode) { 32 | $this->Lexer->addSpecialPattern(']*>', $aMode, 'plugin_nspages'); 33 | } 34 | 35 | function getSort() { 36 | //Execute before html mode 37 | return 189; 38 | } 39 | 40 | function getType() { 41 | return 'substition'; 42 | } 43 | 44 | function handle($match, $state, $pos, Doku_Handler $handler) { 45 | $return = $this->_getDefaultOptions(); 46 | $return['pos'] = $pos; 47 | 48 | $match = PhpString::substr($match, 8, -1); //9 = strlen("getConf('global_exclude'), $return['excludedPages'], $return['excludedNS']); 93 | optionParser::checkExclude($match, $return['excludedPages'], $return['excludedNS'], $return['excludeSelfPage']); 94 | optionParser::checkAnchorName($match, $return['anchorName']); 95 | optionParser::checkActualTitle($match, $return['actualTitleLevel']); 96 | optionParser::checkSimpleStringArgument($match, $return['defaultPicture'], $this, 'defaultPicture'); 97 | 98 | //Now, only the wanted namespace remains in $match 99 | $match = strtolower(trim($match)); 100 | if ($return["sidebar"]) { 101 | // Don't bother resolving or sanitizing now: it will be done at render-time in this mode 102 | $return['wantedNS'] = $match; 103 | } else { 104 | $nsFinder = new namespaceFinder($match); 105 | $return['wantedNS'] = $nsFinder->getWantedNs(); 106 | $return['safe'] = $nsFinder->isNsSafe(); 107 | $return['wantedDir'] = $nsFinder->getWantedDirectory(); 108 | } 109 | 110 | return $return; 111 | } 112 | 113 | private function _getDefaultOptions(){ 114 | return array( 115 | 'subns' => false, 'nopages' => false, 'simpleList' => false, 'lineBreak' => false, 116 | 'excludedPages' => array(), 'excludedNS' => array(), 'excludeSelfPage' => false, 117 | 'title' => false, 'wantedNS' => '', 'wantedDir' => '', 'safe' => true, 118 | 'textNS' => '', 'textPages' => '', 'pregPagesOn' => array(), 119 | 'customTitle' => null, 120 | 'pregPagesOff' => array(), 'pregNSOn' => array(), 'pregNSOff' => array(), 121 | 'pregPagesTitleOn' => array(), 'pregPagesTitleOff' => array(), 122 | 'pregNSTitleOn' => array(), 'pregNSTitleOff' => array(), 123 | 'maxDepth' => (int) 1, 'nbCol' => 3, 'simpleLine' => false, 124 | 'sortid' => false, 'reverse' => false, 125 | 'pagesinns' => false, 'anchorName' => null, 'actualTitleLevel' => false, 126 | 'idAndTitle' => false, 'nbItemsMax' => 0, 'numberedList' => false, 127 | 'natOrder' => false, 'sortDate' => false, 128 | 'hidenopages' => false, 'hidenons' => false, 'hidenosubns' => false, 'usePictures' => false, 129 | 'showhidden' => false, 'dictOrder' => false, 130 | 'modificationDateOnPictures' => false, 131 | 'displayModificationDate' => false, 132 | 'sortByCreationDate' => false, 'defaultPicture' => null, 'tree' => false, 133 | 'includeItemsInTOC' => false, 'sidebar' => false, 134 | ); 135 | } 136 | 137 | function render($mode, Doku_Renderer $renderer, $data) { 138 | $this->_deactivateTheCacheIfNeeded($renderer); 139 | 140 | if ($data['modificationDateOnPictures']){ 141 | action_plugin_nspages::logUseLegacySyntax(); 142 | } 143 | 144 | //Load lang now rather than at handle-time, otherwise it doesn't 145 | //behave well with the translation plugin (it seems like we cache strings 146 | //even if the lang doesn't match) 147 | $this->_denullifyLangOptions($data); 148 | $this->_denullifyPictureOptions($data); 149 | $printer = $this->_selectPrinter($mode, $renderer, $data); 150 | 151 | if ($data['sidebar']) { 152 | if ($data['wantedNS'] !== '') { 153 | $printer->printErrorSidebarDoestAcceptNamespace($data['wantedNS']); 154 | return TRUE; 155 | } 156 | if ($mode === "metadata") { 157 | // In this case $INFO is null so there is not much we can do, 158 | // but anyway in "sidebar" mode we're not really going to generate metadata of the current page 159 | // so it rather makes sense to exiting without printing anything. 160 | return TRUE; 161 | } 162 | global $INFO; 163 | $data['wantedNS'] = $INFO['namespace']; 164 | $data['safe'] = true; 165 | $data['wantedDir'] = namespaceFinder::namespaceToDirectory($data['wantedNS']); 166 | } 167 | 168 | 169 | if( ! $this->_isNamespaceUsable($data)) 170 | { 171 | if( ! $data['hidenons']) { 172 | $printer->printUnusableNamespace($data['wantedNS']); 173 | } 174 | return TRUE; 175 | } 176 | 177 | $fileHelper = new fileHelper($data, $this->getConf('custom_title_allow_list_metadata')); 178 | $pages = $fileHelper->getPages(); 179 | $subnamespaces = $fileHelper->getSubnamespaces(); 180 | if ( $this->_shouldPrintPagesAmongNamespaces($data) ){ 181 | $subnamespaces = array_merge($subnamespaces, $pages); 182 | } 183 | 184 | $printer->printBeginning(); 185 | $this->_print($printer, $data, $subnamespaces, $pages); 186 | $printer->printEnd(); 187 | return TRUE; 188 | } 189 | 190 | function _denullifyLangOptions(&$data){ 191 | if ( is_null($data['textNS']) ){ 192 | $data['textNS'] = $this->getLang('subcats'); 193 | } 194 | 195 | if ( is_null($data['textPages']) ){ 196 | $data['textPages'] = $this->getLang('pagesinthiscat'); 197 | } 198 | } 199 | 200 | function _denullifyPictureOptions(&$data){ 201 | if ( is_null($data['defaultPicture']) ){ 202 | $data['defaultPicture'] = $this->getConf('default_picture'); 203 | } 204 | } 205 | 206 | private function _shouldPrintPagesAmongNamespaces($data){ 207 | return $data['pagesinns']; 208 | } 209 | 210 | private function _print($printer, $data, $subnamespaces, $pages){ 211 | if($data['subns']) { 212 | $printer->printTOC($subnamespaces, 'subns', $data['textNS'], $data['hidenosubns']); 213 | } 214 | 215 | if(!$this->_shouldPrintPagesAmongNamespaces($data)) { 216 | 217 | if ( $this->_shouldPrintTransition($data) ){ 218 | $printer->printTransition(); 219 | } 220 | 221 | if(!$data['nopages']) { 222 | $printer->printTOC($pages, 'page', $data['textPages'], $data['hidenopages']); 223 | } 224 | } 225 | } 226 | 227 | private function _shouldPrintTransition($data){ 228 | return $data['textPages'] === '' && !$data['nopages'] && $data['subns']; 229 | } 230 | 231 | private function _isNamespaceUsable($data){ 232 | global $conf; 233 | return @opendir($conf['datadir'] . '/' . $data['wantedDir']) !== false && $data['safe']; 234 | } 235 | 236 | private function _selectPrinter($mode, &$renderer, $data){ 237 | if($data['simpleList']) { 238 | return new nspages_printerSimpleList($this, $mode, $renderer, $data); 239 | } else if($data['numberedList']){ 240 | return new nspages_printerSimpleList($this, $mode, $renderer, $data, true); 241 | } else if($data['simpleLine']) { 242 | return new nspages_printerOneLine($this, $mode, $renderer, $data); 243 | } else if ($data['lineBreak']){ 244 | return new nspages_printerLineBreak($this, $mode, $renderer, $data); 245 | } else if ($data['usePictures'] && $mode == 'xhtml') { //This printer doesn't support non html mode yet 246 | return new nspages_printerPictures($this, $mode, $renderer, $data); 247 | } else if ($data['tree']) { 248 | return new nspages_printerTree($this, $mode, $renderer, $data); 249 | } else if($mode == 'xhtml') { 250 | return new nspages_printerNice($this, $mode, $renderer, $data['nbCol'], $data['anchorName'], $data); 251 | } 252 | return new nspages_printerSimpleList($this, $mode, $renderer, $data); 253 | } 254 | 255 | private function _deactivateTheCacheIfNeeded(&$renderer) { 256 | if ($this->getConf('cache') == 1){ 257 | $renderer->nocache(); //disable cache 258 | } 259 | } 260 | } 261 | --------------------------------------------------------------------------------