From acd427b2ce2ba86fed2784e743a5eca22caa0b4b Mon Sep 17 00:00:00 2001 From: luxick Date: Fri, 20 Feb 2026 18:02:05 +0100 Subject: [PATCH] Remove unused code --- LICENSE | 339 ------------- _test/ChronoIDTest.php | 138 ------ _test/ChronologicalDateAutoLinkerTest.php | 42 -- _test/ChronologicalDayTemplateTest.php | 37 -- _test/ChronologicalIcsEventsTest.php | 222 --------- _test/GeneralTest.php | 82 ---- _test/PathTest.php | 134 ------ _test/ScratchpadMapTest.php | 44 -- _test/SyntaxTest.php | 524 --------------------- _test/filelistdata/example.txt | 1 - _test/filelistdata/exampledir/example2.txt | 1 - _test/filelistdata/exampleimage.png | Bin 1134 -> 0 bytes _test/filelistdata/~$ignoreme.docx | 0 action.php | 27 -- file.php | 2 +- pagelink.php | 2 +- scratchpad.php | 2 +- temp-input-colors.css | 22 - 18 files changed, 3 insertions(+), 1616 deletions(-) delete mode 100644 LICENSE delete mode 100644 _test/ChronoIDTest.php delete mode 100644 _test/ChronologicalDateAutoLinkerTest.php delete mode 100644 _test/ChronologicalDayTemplateTest.php delete mode 100644 _test/ChronologicalIcsEventsTest.php delete mode 100644 _test/GeneralTest.php delete mode 100644 _test/PathTest.php delete mode 100644 _test/ScratchpadMapTest.php delete mode 100644 _test/SyntaxTest.php delete mode 100644 _test/filelistdata/example.txt delete mode 100644 _test/filelistdata/exampledir/example2.txt delete mode 100644 _test/filelistdata/exampleimage.png delete mode 100644 _test/filelistdata/~$ignoreme.docx delete mode 100644 temp-input-colors.css diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d159169..0000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/_test/ChronoIDTest.php b/_test/ChronoIDTest.php deleted file mode 100644 index d15df81..0000000 --- a/_test/ChronoIDTest.php +++ /dev/null @@ -1,138 +0,0 @@ -assertBool(true, ChronoID::isIsoDate($date), 'Expected valid ISO date: ' . $date); - } - } - - public function testIsIsoDateInvalidCases(): void - { - $invalid = [ - '2023-02-29', - '2024-13-01', - '2024-00-10', - '24-10-2024', - '2024/10/24', - '2024-10-24 12:00:00', - '2024-10-24T12:00:00', - '0000-01-01', - ]; - - foreach ($invalid as $date) { - $this->assertBool(false, ChronoID::isIsoDate($date), 'Expected invalid ISO date: ' . $date); - } - } - - public function testDateToDayId(): void - { - $this->assertStringOrNull('chronological:2024:10:24', ChronoID::dateToDayId('2024-10-24'), 'dateToDayId failed'); - $this->assertStringOrNull('journal:chrono:2024:10:24', ChronoID::dateToDayId('2024-10-24', 'journal:chrono'), 'dateToDayId with custom namespace failed'); - - $this->assertStringOrNull(null, ChronoID::dateToDayId('2024-10-24T12:00:00'), 'datetime should be rejected'); - $this->assertStringOrNull(null, ChronoID::dateToDayId('2024-13-01'), 'invalid month should be rejected'); - $this->assertStringOrNull(null, ChronoID::dateToDayId('2024-10-24', ''), 'empty namespace should be rejected'); - $this->assertStringOrNull(null, ChronoID::dateToDayId('2024-10-24', 'bad namespace!'), 'invalid namespace should be rejected'); - } - - public function testCanonicalIdChecks(): void - { - $this->assertBool(true, ChronoID::isDayId('chronological:2024:10:24'), 'valid day ID should be accepted'); - $this->assertBool(true, ChronoID::isMonthId('chronological:2024:10'), 'valid month ID should be accepted'); - $this->assertBool(true, ChronoID::isYearId('chronological:2024'), 'valid year ID should be accepted'); - - $this->assertBool(false, ChronoID::isDayId('2024:10:24'), 'missing namespace should be rejected as day ID'); - $this->assertBool(false, ChronoID::isDayId('chronological:2024-10-24'), 'hyphen date in ID should be rejected as day ID'); - $this->assertBool(false, ChronoID::isDayId('chronological:2023:02:29'), 'invalid Gregorian day should be rejected'); - - $this->assertBool(false, ChronoID::isMonthId('chronological:2024:13'), 'invalid month 13 should be rejected'); - $this->assertBool(false, ChronoID::isMonthId('chronological:2024:00'), 'invalid month 00 should be rejected'); - $this->assertBool(false, ChronoID::isMonthId('chronological:2024-10'), 'invalid month format should be rejected'); - - $this->assertBool(false, ChronoID::isYearId('chronological:0000'), 'year 0000 should be rejected'); - $this->assertBool(false, ChronoID::isYearId('chronological:24'), 'short year should be rejected'); - $this->assertBool(false, ChronoID::isYearId('chronological:2024:10'), 'month ID should not pass as year ID'); - } - - public function testConversions(): void - { - $this->assertStringOrNull('chronological:2024:10', ChronoID::dayIdToMonthId('chronological:2024:10:24'), 'dayIdToMonthId failed'); - $this->assertStringOrNull('chronological:2024', ChronoID::monthIdToYearId('chronological:2024:10'), 'monthIdToYearId failed'); - - $this->assertStringOrNull(null, ChronoID::dayIdToMonthId('chronological:2024:13:24'), 'invalid day ID should map to null month ID'); - $this->assertStringOrNull(null, ChronoID::dayIdToMonthId('chronological:2024:10'), 'month ID should not map via dayIdToMonthId'); - $this->assertStringOrNull(null, ChronoID::monthIdToYearId('chronological:2024:13'), 'invalid month ID should map to null year ID'); - $this->assertStringOrNull(null, ChronoID::monthIdToYearId('chronological:2024:10:24'), 'day ID should not map via monthIdToYearId'); - } - - /** - * Integration-style smoke test for canonical ID matrix acceptance/rejection. - */ - public function testCanonicalPageIdSmokeMatrix(): void - { - $accepted = [ - ['day', 'chronological:2024:10:24'], - ['month', 'chronological:2024:10'], - ['year', 'chronological:2024'], - ]; - - foreach ($accepted as [$kind, $id]) { - if ($kind === 'day') { - $this->assertBool(true, ChronoID::isDayId($id), 'Expected accepted day ID: ' . $id); - } elseif ($kind === 'month') { - $this->assertBool(true, ChronoID::isMonthId($id), 'Expected accepted month ID: ' . $id); - } else { - $this->assertBool(true, ChronoID::isYearId($id), 'Expected accepted year ID: ' . $id); - } - } - - $rejected = [ - '2024:10:24', - 'chronological:2024-10-24', - 'chronological:2024:13:01', - 'chronological:2024:00', - 'chronological:0000', - ]; - - foreach ($rejected as $id) { - $this->assertBool(false, ChronoID::isDayId($id), 'Unexpected day ID acceptance: ' . $id); - $this->assertBool(false, ChronoID::isMonthId($id), 'Unexpected month ID acceptance: ' . $id); - $this->assertBool(false, ChronoID::isYearId($id), 'Unexpected year ID acceptance: ' . $id); - } - } -} diff --git a/_test/ChronologicalDateAutoLinkerTest.php b/_test/ChronologicalDateAutoLinkerTest.php deleted file mode 100644 index 93744d0..0000000 --- a/_test/ChronologicalDateAutoLinkerTest.php +++ /dev/null @@ -1,42 +0,0 @@ -Meeting on 2024-10-24

'; - $out = ChronologicalDateAutoLinker::linkHtml($html); - - $decoded = urldecode($out); - if (strpos($decoded, 'chronological:2024:10:24') === false) { - throw new \Exception('Expected canonical link target not found'); - } - } - - public function testSkipsCodeContent(): void - { - $html = '

Outside 2024-10-25

Inside 2024-10-24'; - $out = ChronologicalDateAutoLinker::linkHtml($html); - - $decoded = urldecode($out); - if (strpos($decoded, 'chronological:2024:10:25') === false) { - throw new \Exception('Expected outside date link not found'); - } - if (strpos($decoded, 'chronological:2024:10:24') !== false) { - throw new \Exception('Date inside code block should not be auto-linked'); - } - } -} diff --git a/_test/ChronologicalDayTemplateTest.php b/_test/ChronologicalDayTemplateTest.php deleted file mode 100644 index 9c5ff1a..0000000 --- a/_test/ChronologicalDayTemplateTest.php +++ /dev/null @@ -1,37 +0,0 @@ - (string)$e['summary'], $events); - if (!in_array('Ganztag Event', $summaries, true)) { - throw new \Exception('Missing all-day event summary'); - } - if (!in_array('Termin A', $summaries, true)) { - throw new \Exception('Missing timed event summary'); - } - - $timed = null; - foreach ($events as $event) { - if ((string)($event['summary'] ?? '') === 'Termin A') { - $timed = $event; - break; - } - } - - if (!is_array($timed)) { - throw new \Exception('Timed event payload missing'); - } - if (trim((string)($timed['startIso'] ?? '')) === '') { - throw new \Exception('Timed event should expose startIso for client-side timezone conversion'); - } - } - - public function testEventsForDateReadsMultipleConfiguredFiles(): void - { - $dir = TMP_DIR . '/chrono_ics/' . uniqid('multi_', true); - @mkdir($dir, 0777, true); - $ics1 = $dir . '/one.ics'; - $ics2 = $dir . '/two.ics'; - - @file_put_contents($ics1, "BEGIN:VCALENDAR\nBEGIN:VEVENT\nDTSTART:20260218T100000\nSUMMARY:Eins\nEND:VEVENT\nEND:VCALENDAR\n"); - @file_put_contents($ics2, "BEGIN:VCALENDAR\nBEGIN:VEVENT\nDTSTART:20260218T110000\nSUMMARY:Zwei\nEND:VEVENT\nEND:VCALENDAR\n"); - - $config = $ics1 . "\n" . $ics2; - $events = ChronologicalIcsEvents::eventsForDate($config, '2026-02-18'); - - if (count($events) !== 2) { - throw new \Exception('Expected 2 events from two files, got ' . count($events)); - } - } - - public function testEventsForDateSupportsWeeklyRecurrence(): void - { - $dir = TMP_DIR . '/chrono_ics/' . uniqid('rrule_', true); - @mkdir($dir, 0777, true); - $ics = $dir . '/recurring.ics'; - - $content = "BEGIN:VCALENDAR\n" - . "BEGIN:VEVENT\n" - . "UID:weekly-1\n" - . "DTSTART:20260205T090000\n" - . "RRULE:FREQ=WEEKLY;INTERVAL=1\n" - . "SUMMARY:Wiederkehrender Termin\n" - . "END:VEVENT\n" - . "END:VCALENDAR\n"; - @file_put_contents($ics, $content); - - $events = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-12'); - if (count($events) < 1) { - throw new \Exception('Expected recurring event on 2026-02-12, got none'); - } - - $summaries = array_map(static fn(array $e): string => (string)$e['summary'], $events); - if (!in_array('Wiederkehrender Termin', $summaries, true)) { - throw new \Exception('Recurring summary not found on matching date'); - } - } - - public function testEventsForDateRespectsExdateForRecurringEvent(): void - { - $dir = TMP_DIR . '/chrono_ics/' . uniqid('exdate_', true); - @mkdir($dir, 0777, true); - $ics = $dir . '/recurring-exdate.ics'; - - $content = "BEGIN:VCALENDAR\n" - . "BEGIN:VEVENT\n" - . "UID:weekly-2\n" - . "DTSTART:20260205T090000\n" - . "RRULE:FREQ=WEEKLY;COUNT=4\n" - . "EXDATE:20260212T090000\n" - . "SUMMARY:Termin mit Ausnahme\n" - . "END:VEVENT\n" - . "END:VCALENDAR\n"; - @file_put_contents($ics, $content); - - $events = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-12'); - $summaries = array_map(static fn(array $e): string => (string)$e['summary'], $events); - if (in_array('Termin mit Ausnahme', $summaries, true)) { - throw new \Exception('Recurring event with EXDATE should not appear on excluded day'); - } - } - - public function testEventsForDateKeepsUtcDateAndTimeAsIs(): void - { - $previousTimezone = date_default_timezone_get(); - date_default_timezone_set('Europe/Berlin'); - - try { - $dir = TMP_DIR . '/chrono_ics/' . uniqid('tz_', true); - @mkdir($dir, 0777, true); - $ics = $dir . '/timezone.ics'; - - $content = "BEGIN:VCALENDAR\n" - . "BEGIN:VEVENT\n" - . "UID:utc-shift\n" - . "DTSTART:20260216T233000Z\n" - . "SUMMARY:UTC Spaet\n" - . "END:VEVENT\n" - . "END:VCALENDAR\n"; - @file_put_contents($ics, $content); - - $eventsOn16 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-16'); - $eventsOn17 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-17'); - - $summaries16 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn16); - $summaries17 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn17); - - if (!in_array('UTC Spaet', $summaries16, true)) { - throw new \Exception('UTC event should stay on its own UTC date'); - } - if (in_array('UTC Spaet', $summaries17, true)) { - throw new \Exception('UTC event should not be shifted to next day by server timezone'); - } - - $utcEvent = null; - foreach ($eventsOn16 as $entry) { - if ((string)($entry['summary'] ?? '') === 'UTC Spaet') { - $utcEvent = $entry; - break; - } - } - if (!is_array($utcEvent)) { - throw new \Exception('UTC event payload missing after day match'); - } - if ((string)($utcEvent['time'] ?? '') !== '23:30') { - throw new \Exception('UTC event time should remain unchanged (expected 23:30)'); - } - } finally { - date_default_timezone_set($previousTimezone); - } - } - - public function testEventsForDateShowsMultiDayAllDayEventOnOverlappingDays(): void - { - $dir = TMP_DIR . '/chrono_ics/' . uniqid('multiday_', true); - @mkdir($dir, 0777, true); - $ics = $dir . '/multiday.ics'; - - $content = "BEGIN:VCALENDAR\n" - . "BEGIN:VEVENT\n" - . "UID:multi-day-1\n" - . "DTSTART;VALUE=DATE:20260216\n" - . "DTEND;VALUE=DATE:20260218\n" - . "SUMMARY:Mehrtagesereignis\n" - . "END:VEVENT\n" - . "END:VCALENDAR\n"; - @file_put_contents($ics, $content); - - $eventsOn16 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-16'); - $eventsOn17 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-17'); - $eventsOn18 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-18'); - - $summaries16 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn16); - $summaries17 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn17); - $summaries18 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn18); - - if (!in_array('Mehrtagesereignis', $summaries16, true)) { - throw new \Exception('Multi-day all-day event should appear on start day'); - } - if (!in_array('Mehrtagesereignis', $summaries17, true)) { - throw new \Exception('Multi-day all-day event should appear on overlapping day'); - } - if (in_array('Mehrtagesereignis', $summaries18, true)) { - throw new \Exception('Multi-day all-day event should respect exclusive DTEND day'); - } - } -} diff --git a/_test/GeneralTest.php b/_test/GeneralTest.php deleted file mode 100644 index 2e46184..0000000 --- a/_test/GeneralTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertFileExists($file); - - $info = confToHash($file); - - $this->assertArrayHasKey('base', $info); - $this->assertArrayHasKey('author', $info); - $this->assertArrayHasKey('email', $info); - $this->assertArrayHasKey('date', $info); - $this->assertArrayHasKey('name', $info); - $this->assertArrayHasKey('desc', $info); - $this->assertArrayHasKey('url', $info); - - $this->assertEquals('luxtools', $info['base']); - $this->assertRegExp('/^https?:\/\//', $info['url']); - $this->assertTrue(mail_isvalid($info['email'])); - $this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']); - $this->assertTrue(false !== strtotime($info['date'])); - } - - /** - * luxtools settings are managed via the plugin's admin page, not via the Configuration Manager. - * Ensure default config exists and (when present) metadata.php does not expose any settings. - */ - public function testPluginConf(): void - { - $conf_file = __DIR__ . '/../conf/default.php'; - $meta_file = __DIR__ . '/../conf/metadata.php'; - - if (!file_exists($conf_file)) { - self::markTestSkipped('No config default.php exists -> skipping test'); - } - - $conf = null; - $meta = null; - - include($conf_file); - $this->assertIsArray( - $conf, - 'The ' . DOKU_PLUGIN . 'luxtools/conf/default.php file needs to define $conf as an array.' - ); - - if (file_exists($meta_file)) { - include($meta_file); - - if ($meta === null) { - // If the file exists but does not define $meta, treat it as empty. - $meta = []; - } - - $this->assertIsArray( - $meta, - 'The ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php file needs to define $meta as an array.' - ); - $this->assertEmpty( - $meta, - 'luxtools should not expose settings via the Configuration Manager.' - ); - } - - } -} diff --git a/_test/PathTest.php b/_test/PathTest.php deleted file mode 100644 index e8dc194..0000000 --- a/_test/PathTest.php +++ /dev/null @@ -1,134 +0,0 @@ -path = new Path( - << alias -EOT - ); - } - - /** - * Test the configuration parsing for paths and aliases - */ - public function testGetPaths() - { - $expect = [ - 'C:/xampp/htdocs/wiki/' => [ - 'root' => 'C:/xampp/htdocs/wiki/', - 'web' => '/lib/plugins/luxtools/file.php?root=C%3A%2Fxampp%2Fhtdocs%2Fwiki%2F&file=', - ], - '\\\\server/share/path/' => [ - 'root' => '\\\\server/share/path/', - 'web' => '/lib/plugins/luxtools/file.php?root=%5C%5Cserver%2Fshare%2Fpath%2F&file=', - ], - '/linux/file/path/' => [ - 'root' => '/linux/file/path/', - 'web' => '/lib/plugins/luxtools/file.php?root=%2Flinux%2Ffile%2Fpath%2F&file=', - ], - '/linux/another/path/' => [ - 'root' => '/linux/another/path/', - 'alias' => 'alias/', - 'web' => '/lib/plugins/luxtools/file.php?root=%2Flinux%2Fanother%2Fpath%2F&file=', - ], - 'alias/' => [ - 'root' => '/linux/another/path/', - 'alias' => 'alias/', - 'web' => '/lib/plugins/luxtools/file.php?root=%2Flinux%2Fanother%2Fpath%2F&file=', - ], - ]; - - $this->assertEquals($expect, $this->path->getPaths()); - } - - /** - * Data provider for testGetPathInfoSuccess - */ - public function providePathInfoSuccess() - { - return [ - ['/linux/another/path', '/linux/another/path/'], - ['/linux/another/path/foo', '/linux/another/path/foo/'], - ['alias', '/linux/another/path/'], - ['alias/foo', '/linux/another/path/foo/'], - ['C:\\xampp\\htdocs\\wiki', 'C:/xampp/htdocs/wiki/'], - ['C:\\xampp\\htdocs\\wiki\\foo', 'C:/xampp/htdocs/wiki/foo/'], - ['\\\\server\\share\\path\\', '\\\\server/share/path/'], - ['\\\\server\\share\\path\\foo', '\\\\server/share/path/foo/'], - ]; - } - - /** - * @dataProvider providePathInfoSuccess - */ - public function testGetPathInfoSuccess($path, $expect) - { - $pathInfo = $this->path->getPathInfo($path); - $this->assertEquals($expect, $pathInfo['path']); - } - - public function providePathInfoFailure() - { - return [ - ['/linux/file/path/../../../etc/'], - ['W:\\xampp\\htdocs\\wiki\\foo\\bar'], - ['/'], - ['./'], - ['../'], - ]; - } - - /** - * @dataProvider providePathInfoFailure - */ - public function testGetPathInfoFailure($path) - { - $this->expectExceptionMessageMatches('/Path not allowed/'); - $this->path->getPathInfo($path); - } - - public function testMapToAliasPath() - { - $mapped = $this->path->mapToAliasPath('/linux/another/path/some/folder'); - $this->assertEquals('alias>some/folder', $mapped); - - $mappedRoot = $this->path->mapToAliasPath('/linux/another/path/'); - $this->assertEquals('alias>', $mappedRoot); - - $unmapped = $this->path->mapToAliasPath('/linux/file/path/example.txt'); - $this->assertEquals('/linux/file/path/example.txt', $unmapped); - } - - public function testMapToAliasPathLegacyAliasStyle() - { - $path = new Path("/srv/share/Datascape/\nA> /Scape/\n"); - - $mapped = $path->mapToAliasPath('/srv/share/Datascape/projects/demo'); - $this->assertEquals('Scape>projects/demo', $mapped); - } -} diff --git a/_test/ScratchpadMapTest.php b/_test/ScratchpadMapTest.php deleted file mode 100644 index 6a5fb7e..0000000 --- a/_test/ScratchpadMapTest.php +++ /dev/null @@ -1,44 +0,0 @@ - start - -C:\\pads\\notes.md -A> notes - -\\\\server\\share\\pads\\team.txt -A> team -EOT - ); - - $this->assertEquals('/var/scratchpads/startpad.txt', $map->resolve('start')); - $this->assertEquals('C:/pads/notes.md', $map->resolve('notes')); - $this->assertEquals('\\\\server/share/pads/team.txt', $map->resolve('team')); - } - - public function testUnknownAliasThrows() - { - $map = new ScratchpadMap("/tmp/pad.txt\nA> pad\n"); - $this->expectExceptionMessageMatches('/Unknown scratchpad alias/'); - $map->resolve('nope'); - } -} diff --git a/_test/SyntaxTest.php b/_test/SyntaxTest.php deleted file mode 100644 index b00c4bf..0000000 --- a/_test/SyntaxTest.php +++ /dev/null @@ -1,524 +0,0 @@ -pluginsEnabled[] = 'luxtools'; - parent::setUp(); - - // Setup config so that access to the TMP directory will be allowed - // Use the built-in file.php endpoint. - $conf ['plugin']['luxtools']['paths'] = TMP_DIR . '/filelistdata/' . "\n" . 'A> /Scape'; - - } - - public static function setUpBeforeClass(): void - { - parent::setUpBeforeClass(); - - // copy test files to test directory - \TestUtils::rcopy(TMP_DIR, dirname(__FILE__) . '/filelistdata'); - } - - /** - * Run a list of checks on the given document - * - * @param Document $doc - * @param array $structure Array of selectors and expected count or content - * @return void - */ - protected function structureCheck(Document $doc, $structure) - { - foreach ($structure as $selector => $expected) { - if (is_numeric($expected)) { - $this->assertEquals( - $expected, - $doc->find($selector)->count(), - 'Selector ' . $selector . ' not found' - ); - } else { - $this->assertStringContainsString( - $expected, - $doc->find($selector)->text(), - 'Selector ' . $selector . ' not found' - ); - }; - } - } - - - /** - * This function checks that all files are listed in not recursive mode. - * Uses {{files>...}} syntax for backwards compatibility (now handled by directory syntax). - */ - public function test_not_recursive() - { - global $conf; - - // Render filelist using files syntax (now handled by directory plugin) - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&direct=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - // We should find: - // - example.txt - // - exampleimage.png - $result = strpos($xhtml, 'example.txt'); - $this->assertFalse($result === false, '"example.txt" not listed'); - $result = strpos($xhtml, 'exampleimage.png'); - $this->assertFalse($result === false, '"exampleimage.png" not listed'); - } - - /** - * This function checks that all files are listed in recursive mode. - * Uses {{files>...}} syntax for backwards compatibility (now handled by directory syntax). - */ - public function test_recursive() - { - // Render filelist using files syntax (now handled by directory plugin) - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&direct=1&recursive=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - // We should find: - // - exampledir - // - example2.txt - // - example.txt - // - exampleimage.png - $result = strpos($xhtml, 'exampledir'); - $this->assertFalse($result === false, '"exampledir" not listed'); - $result = strpos($xhtml, 'example2.txt'); - $this->assertFalse($result === false, '"example2.txt" not listed'); - $result = strpos($xhtml, 'example.txt'); - $this->assertFalse($result === false, '"example.txt" not listed'); - $result = strpos($xhtml, 'exampleimage.png'); - $this->assertFalse($result === false, '"exampleimage.png" not listed'); - } - - /** - * This function checks the rendering when style=list is explicitly specified. - * Note: The files syntax is now handled by directory syntax and always renders as table. - * This test is kept for backwards compatibility testing but expects table structure. - */ - public function testUnorderedList() - { - // Render filelist with explicit style=list (now ignored, renders as table) - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - // Now renders as a table instead of list - $structure = [ - 'div.luxtools-plugin' => 1, - 'div.luxtools-plugin table' => 1, - ]; - - $this->structureCheck($doc, $structure); - } - - /** - * This function checks the rendering when style=olist is explicitly specified. - * Note: The files syntax is now handled by directory syntax and always renders as table. - * This test is kept for backwards compatibility testing but expects table structure. - */ - public function testOrderedList() - { - // Render filelist with explicit style=olist (now ignored, renders as table) - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=olist&direct=1&recursive=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - // Now renders as a table instead of ordered list - $structure = [ - 'div.luxtools-plugin' => 1, - 'div.luxtools-plugin table' => 1, - ]; - - $this->structureCheck($doc, $structure); - } - - /** - * This function checks that the table mode - * generates the expected XHTML structure. - */ - public function test_table() - { - global $conf; - - // Render filelist - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=table&direct=1&recursive=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'div.luxtools-plugin' => 1, - 'div.luxtools-plugin table' => 1, - 'div.luxtools-plugin table > tbody > tr' => 3, - 'div.luxtools-plugin table > tbody > tr:nth-child(1) a' => 'example.txt', - 'div.luxtools-plugin table > tbody > tr:nth-child(2) a' => 'exampledir/example2.txt', - 'div.luxtools-plugin table > tbody > tr:nth-child(3) a' => 'exampleimage.png', - ]; - - $this->structureCheck($doc, $structure); - } - - public function test_default_maxheight_applies_scroll() - { - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $style = (string)$doc->find('div.luxtools-plugin')->attr('style'); - - $this->assertStringContainsString('max-height: 500px', $style); - $this->assertStringContainsString('overflow-y: auto', $style); - } - - public function test_maxheight_can_be_disabled() - { - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=table&direct=1&recursive=1&maxheight=-1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $style = $doc->find('div.luxtools-plugin')->attr('style'); - - $this->assertTrue($style === null || $style === ''); - } - - /** - * This function checks that the images syntax renders a thumbnail gallery. - */ - public function test_images_gallery() - { - $instructions = p_get_instructions('{{images>' . TMP_DIR . '/filelistdata/*&direct=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'div.luxtools-plugin.luxtools-gallery' => 1, - 'div.luxtools-plugin.luxtools-gallery a' => 1, - 'div.luxtools-plugin.luxtools-gallery img' => 1, - ]; - $this->structureCheck($doc, $structure); - - $this->assertStringContainsString('exampleimage.png', $xhtml); - $this->assertStringContainsString('thumb=1', $xhtml); - $this->assertStringContainsString('width="150"', $xhtml); - $this->assertStringContainsString('height="150"', $xhtml); - } - - /** - * Grouping wrapper should use default flex mode with zero gap. - */ - public function test_grouping_default_flex() - { - $imagePath = TMP_DIR . '/filelistdata/exampleimage.png'; - $syntax = '' - . '{{image>' . $imagePath . '|One|120}}' - . '{{image>' . $imagePath . '|Two|120}}' - . ''; - - $instructions = p_get_instructions($syntax); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'div.luxtools-grouping.luxtools-grouping--flex' => 1, - 'div.luxtools-grouping .luxtools-imagebox' => 2, - ]; - $this->structureCheck($doc, $structure); - - $style = (string)$doc->find('div.luxtools-grouping')->first()->attr('style'); - $this->assertStringContainsString('--luxtools-grouping-cols: 2', $style); - $this->assertStringContainsString('--luxtools-grouping-gap: 0', $style); - $this->assertStringContainsString('--luxtools-grouping-justify: start', $style); - $this->assertStringContainsString('--luxtools-grouping-align: start', $style); - } - - /** - * Grouping wrapper should accept custom flex layout and gap. - */ - public function test_grouping_custom_flex() - { - $imagePath = TMP_DIR . '/filelistdata/exampleimage.png'; - $syntax = '' - . '{{image>' . $imagePath . '|One|120}}' - . '{{image>' . $imagePath . '|Two|120}}' - . ''; - - $instructions = p_get_instructions($syntax); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'div.luxtools-grouping.luxtools-grouping--flex' => 1, - 'div.luxtools-grouping .luxtools-imagebox' => 2, - ]; - $this->structureCheck($doc, $structure); - - $style = (string)$doc->find('div.luxtools-grouping')->first()->attr('style'); - $this->assertStringContainsString('--luxtools-grouping-gap: 8px', $style); - } - - /** - * Grouping wrapper should accept justify and align controls. - */ - public function test_grouping_justify_and_align() - { - $imagePath = TMP_DIR . '/filelistdata/exampleimage.png'; - $syntax = '' - . '{{image>' . $imagePath . '|One|120}}' - . '{{image>' . $imagePath . '|Two|120}}' - . ''; - - $instructions = p_get_instructions($syntax); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $style = (string)$doc->find('div.luxtools-grouping')->first()->attr('style'); - $this->assertStringContainsString('--luxtools-grouping-justify: space-between', $style); - $this->assertStringContainsString('--luxtools-grouping-align: center', $style); - } - - /** - * Unknown grouping attributes should render a warning string. - */ - public function test_grouping_unknown_option_warning() - { - $imagePath = TMP_DIR . '/filelistdata/exampleimage.png'; - $syntax = '' - . '{{image>' . $imagePath . '|One|120}}' - . ''; - - $instructions = p_get_instructions($syntax); - $xhtml = p_render('xhtml', $instructions, $info); - - $this->assertStringContainsString('[grouping: unknown option(s): gpa]', $xhtml); - } - - /** - * Ensure the built-in file endpoint includes the host page id so file.php can - * enforce per-page ACL. - */ - public function test_file_links_include_page_id_for_acl() - { - global $ID; - $ID = 'luxtools:acltest'; - - $instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $href = (string)$doc->find('div.luxtools-plugin a')->first()->attr('href'); - $this->assertNotSame('', $href); - $this->assertStringContainsString('lib/plugins/luxtools/file.php', $href); - $this->assertStringContainsString('id=luxtools%3Aacltest', $href); - } - - /** - * This function checks that the open syntax renders an inline link. - */ - public function test_open_link() - { - $instructions = p_get_instructions('{{open>/tmp/somewhere|Open here}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'a.luxtools-open' => 1, - ]; - $this->structureCheck($doc, $structure); - - $this->assertStringContainsString('Open here', $xhtml); - $this->assertStringContainsString('data-path="/tmp/somewhere"', $xhtml); - } - - /** - * This function checks that the directory syntax renders a flat table, - * listing both folders and files. - */ - public function test_directory_table_flat() - { - $instructions = p_get_instructions('{{directory>' . TMP_DIR . '/filelistdata/&direct=1}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - $doc = new Document(); - $doc->html($xhtml); - - $structure = [ - 'div.luxtools-plugin' => 1, - 'div.luxtools-plugin table' => 1, - 'div.luxtools-plugin table > tbody > tr' => 3, - 'a.luxtools-open' => 1, - ]; - $this->structureCheck($doc, $structure); - - // Should list the top-level entries, but not recurse into exampledir - $this->assertStringContainsString('example.txt', $xhtml); - $this->assertStringContainsString('exampleimage.png', $xhtml); - $this->assertStringContainsString('exampledir', $xhtml); - $this->assertStringNotContainsString('example2.txt', $xhtml); - - // Directory row should trigger the same behaviour as {{open>...}} for that folder - $this->assertStringContainsString('data-path="/Scape/exampledir"', $xhtml); - } - - /** - * Strict ISO dates in plain text should be auto-linked to canonical day IDs. - */ - public function test_auto_link_iso_date_plain_text() - { - $instructions = p_get_instructions('Meeting with John on 2024-10-24.'); - $xhtml = p_render('xhtml', $instructions, $info); - - if (strpos($xhtml, '>2024-10-24') === false) { - throw new \Exception('Auto-link text for 2024-10-24 not found'); - } - - if (strpos(urldecode($xhtml), 'chronological:2024:10:24') === false) { - throw new \Exception('Auto-link target chronological:2024:10:24 not found'); - } - } - - /** - * Auto-linking must not run inside code blocks. - */ - public function test_auto_link_skips_code_blocks() - { - $syntax = 'Outside date 2024-10-25.' . "\n\n" . 'Inside code 2024-10-24'; - $instructions = p_get_instructions($syntax); - $xhtml = p_render('xhtml', $instructions, $info); - - if (strpos($xhtml, '>2024-10-25') === false) { - throw new \Exception('Outside date 2024-10-25 was not auto-linked'); - } - - if (strpos(urldecode($xhtml), 'chronological:2024:10:25') === false) { - throw new \Exception('Outside auto-link target chronological:2024:10:25 not found'); - } - - if (strpos(urldecode($xhtml), 'chronological:2024:10:24') !== false) { - throw new \Exception('Date inside code block was incorrectly auto-linked'); - } - - if (strpos($xhtml, 'Inside code 2024-10-24') === false) { - throw new \Exception('Code block content was unexpectedly altered'); - } - } - - /** - * Calendar widget should render links to canonical day IDs. - */ - public function test_calendar_widget_links_canonical_day_ids() - { - $instructions = p_get_instructions('{{calendar>2024-10}}'); - $xhtml = p_render('xhtml', $instructions, $info); - - if (strpos($xhtml, 'luxtools-calendar') === false) { - throw new \Exception('Calendar container not rendered'); - } - - $decoded = urldecode($xhtml); - if (strpos($decoded, 'chronological:2024:10:01') === false) { - throw new \Exception('Expected canonical day link for 2024-10-01 not found'); - } - if (strpos($decoded, 'chronological:2024:10:31') === false) { - throw new \Exception('Expected canonical day link for 2024-10-31 not found'); - } - - if (strpos($decoded, 'chronological:2024:10') === false) { - throw new \Exception('Expected month link chronological:2024:10 not found in header'); - } - if (strpos($decoded, 'chronological:2024') === false) { - throw new \Exception('Expected year link chronological:2024 not found in header'); - } - if (strpos($decoded, 'chronological:2024:09') === false) { - throw new \Exception('Expected previous month canonical ID chronological:2024:09 not found'); - } - if (strpos($decoded, 'chronological:2024:11') === false) { - throw new \Exception('Expected next month canonical ID chronological:2024:11 not found'); - } - - if (strpos($xhtml, 'luxtools-calendar-nav') === false) { - throw new \Exception('Calendar navigation container not rendered'); - } - - if (strpos($xhtml, 'luxtools-calendar-nav-button') === false) { - throw new \Exception('Calendar navigation buttons not rendered'); - } - - if (strpos($xhtml, 'data-luxtools-calendar="1"') === false) { - throw new \Exception('Calendar JS state attribute not rendered'); - } - if (strpos($xhtml, 'data-luxtools-ajax-url=') === false) { - throw new \Exception('Calendar AJAX endpoint metadata not rendered'); - } - - if (strpos($xhtml, 'luxtools-calendar-day') === false || strpos($xhtml, 'register_hook( - "CSS_STYLES_INCLUDED", - "BEFORE", - $this, - "addTemporaryInputStyles", - ); $controller->register_hook( "AJAX_CALL_UNKNOWN", "BEFORE", @@ -164,27 +158,6 @@ class action_plugin_luxtools extends ActionPlugin echo $html; } - /** - * Include temporary global input styling via css.php so @ini_* placeholders resolve. - * - * @param Event $event - * @param mixed $param - * @return void - */ - public function addTemporaryInputStyles(Event $event, $param) - { - if (!isset($event->data['mediatype']) || $event->data['mediatype'] !== 'screen') { - return; - } - - if (!isset($event->data['files']) || !is_array($event->data['files'])) { - return; - } - - $plugin = $this->getPluginName(); - $event->data['files'][DOKU_PLUGIN . $plugin . '/temp-input-colors.css'] = DOKU_BASE . 'lib/plugins/' . $plugin . '/'; - } - /** * Auto-link strict ISO dates (YYYY-MM-DD) in rendered XHTML text nodes. * diff --git a/file.php b/file.php index b85c0ea..59aed8c 100644 --- a/file.php +++ b/file.php @@ -6,7 +6,7 @@ use dokuwiki\plugin\luxtools\Path; require_once(__DIR__ . '/autoload.php'); -if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../'); +if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/_dokuwiki'); if (!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT', 1); // we gzip ourself here require_once(DOKU_INC . 'inc/init.php'); diff --git a/pagelink.php b/pagelink.php index ee3dd98..801dca2 100644 --- a/pagelink.php +++ b/pagelink.php @@ -6,7 +6,7 @@ use dokuwiki\plugin\luxtools\PageLink; require_once(__DIR__ . '/autoload.php'); -if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../'); +if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/_dokuwiki'); require_once(DOKU_INC . 'inc/init.php'); global $INPUT; diff --git a/scratchpad.php b/scratchpad.php index f8c83f6..e532948 100644 --- a/scratchpad.php +++ b/scratchpad.php @@ -7,7 +7,7 @@ use dokuwiki\plugin\luxtools\ScratchpadMap; require_once(__DIR__ . '/autoload.php'); -if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../'); +if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/_dokuwiki'); require_once(DOKU_INC . 'inc/init.php'); global $INPUT; diff --git a/temp-input-colors.css b/temp-input-colors.css deleted file mode 100644 index 8b529b9..0000000 --- a/temp-input-colors.css +++ /dev/null @@ -1,22 +0,0 @@ -/* TEMPORARY FIX - * Apply template color placeholders to all form controls. - * Remove this file once the template provides proper input styling. - */ - -input, -textarea, -select, -button { - color: @ini_text; - background-color: @ini_background; - border: 1px solid @ini_border; -} - -button:not(.toolbutton) { - background: @ini_background; -} - -select option { - color: @ini_text; - background-color: @ini_background; -}