mikuli.cz

:)
git clone https://git.sr.ht/~ashymad/mikuli.cz
Log | Files | Refs

commit 5c5feb0bea7a3e983a35f43dc658f22b967dec36
parent d15f38582f400caf6dab16480676c9a1b0f4f7e2
Author: Ashymad <szymon.mikulicz@posteo.net>
Date:   Mon,  2 Mar 2026 23:03:57 +0100

Update yellow

Diffstat:
Dlicense.md | 294-------------------------------------------------------------------------------
Dmedia/thumbnails/photo-100x40.jpg | 0
Msystem/extensions/update-available.ini | 11-----------
Msystem/extensions/update-installed.ini | 54+++++++++++++++++++++---------------------------------
Msystem/extensions/yellow-language.ini | 4----
Msystem/extensions/yellow-system.ini | 28+++++++++++++++++++---------
Msystem/extensions/yellow-website.log | 4++++
Msystem/layouts/blog-start.html | 2+-
Msystem/layouts/blog.html | 2+-
Asystem/workers/blog.php | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsystem/workers/install-blog.bin | 0
Dsystem/workers/install-language.bin | 0
Dsystem/workers/install-wiki.bin | 0
Dsystem/workers/install.php | 581-------------------------------------------------------------------------------
Asystem/workers/previousnext.php | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
15 files changed, 365 insertions(+), 934 deletions(-)

diff --git a/license.md b/license.md @@ -1,294 +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. - -## Terms and conditions - - **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. - - **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 this license to this software - -At the start of each source file you see a link to a website, that contains -this license and contact information. For example: - - Datenstrom Yellow, https://github.com/datenstrom/yellow - -If your software is based on modified source code, you have to keep the -original link in each source file. For example: - - Gnomovision Orange, https://github.com/gnomovision/orange - Based on Datenstrom Yellow, https://github.com/datenstrom/yellow - -All source files are copyrighted by the respective author(s) -and licensed under GPL version 2, unless stated otherwise. diff --git a/media/thumbnails/photo-100x40.jpg b/media/thumbnails/photo-100x40.jpg Binary files differ. diff --git a/system/extensions/update-available.ini b/system/extensions/update-available.ini @@ -612,17 +612,6 @@ Published: 2024-04-05 17:22:52 Status: available system/workers/readingtime.php: readingtime.php, create, update -Extension: Readingtime -Version: 0.8.22 -Description: Show estimated reading time for page content. -DownloadUrl: https://github.com/schulle4u/yellow-readingtime/archive/refs/heads/main.zip -DocumentationUrl: https://github.com/schulle4u/yellow-readingtime -DocumentationLanguage: en, de -Published: 2023-05-23 13:25:56 -Developer: Steffen Schultz -Tag: feature, page, blog, readingtime -system/extensions/readingtime.php: readingtime.php, create, update - Extension: Russian Version: 0.9.5 Description: Russian language. diff --git a/system/extensions/update-installed.ini b/system/extensions/update-installed.ini @@ -1,21 +1,22 @@ # Datenstrom Yellow update settings for installed extensions Extension: Blog -Version: 0.8.30 +Version: 0.9.4 Description: Blog for your website. +Developer: Anna Svensson +Tag: feature DownloadUrl: https://github.com/annaesvensson/yellow-blog/archive/refs/heads/main.zip DocumentationUrl: https://github.com/annaesvensson/yellow-blog DocumentationLanguage: en, de, sv -Published: 2023-11-01 17:49:02 -Developer: Anna Svensson -Tag: feature -system/extensions/blog.php: blog.php, create, update +Published: 2024-05-09 16:44:32 +Status: available +system/workers/blog.php: blog.php, create, update system/layouts/blog.html: blog.html, create, update, careful system/layouts/blog-start.html: blog-start.html, create, update, careful content/shared/page-new-blog.md: page-new-blog.md, create, optional -content/2-blog/page.md: page.md, create, optional -content/2-blog/2020-04-07-blog-example-page.md: 2020-04-07-blog-example-page.md, create, optional -content/2-blog/2020-12-06-made-for-people.md: 2020-12-06-made-for-people.md, create, optional +content/3-blog/page.md: page.md, create, optional +content/3-blog/2020-04-07-blog-example-page.md: 2020-04-07-blog-example-page.md, create, optional +content/3-blog/2020-12-06-made-for-people.md: 2020-12-06-made-for-people.md, create, optional Extension: Core Version: 0.9.22 @@ -98,31 +99,6 @@ system/workers/image.php: image.php, create, update media/images/photo.jpg: photo.jpg, create, optional media/thumbnails/photo-100x40.jpg: photo-100x40.jpg, create, optional -Extension: Install -Version: 0.9.10 -Description: Install a brand new website. -Developer: Anna Svensson -DownloadUrl: https://github.com/annaesvensson/yellow-install/archive/refs/heads/main.zip -DocumentationUrl: https://github.com/annaesvensson/yellow-install -DocumentationLanguage: en, de, sv -Published: 2026-02-19 22:47:16 -Status: unassembled -system/workers/install.php: install.php, create -system/workers/install-language.bin: install-language.bin, compress @source/yellow-language/, create -system/workers/install-wiki.bin: install-wiki.bin, compress @source/yellow-wiki/, create -system/workers/install-blog.bin: install-blog.bin, compress @source/yellow-blog/, create -system/extensions/yellow-system.ini: yellow-system.ini, create -system/extensions/yellow-user.ini: yellow-user.ini, create -system/extensions/yellow-language.ini: yellow-language.ini, create -content/1-home/page.md: 1-home-page.md, create -content/9-about/page.md: 9-about-page.md, create -content/shared/page-error-404.md: page-error-404.md, create -media/downloads/yellow-english.pdf: yellow-english.pdf, create -media/downloads/yellow-deutsch.pdf: yellow-deutsch.pdf, create -media/downloads/yellow-svenska.pdf: yellow-svenska.pdf, create -./yellow.php: yellow.php, create -./robots.txt: robots.txt, create - Extension: Markdown Version: 0.9.8 Description: Text formatting for humans. @@ -135,6 +111,18 @@ Published: 2026-02-24 18:30:33 Status: available system/workers/markdown.php: markdown.php, create, update +Extension: Previousnext +Version: 0.9.1 +Description: Show links to previous/next page. +Developer: Anna Svensson +Tag: feature +DownloadUrl: https://github.com/annaesvensson/yellow-previousnext/archive/refs/heads/main.zip +DocumentationUrl: https://github.com/annaesvensson/yellow-previousnext +DocumentationLanguage: en, de, sv +Published: 2024-04-05 00:26:47 +Status: available +system/workers/previousnext.php: previousnext.php, create, update + Extension: Serve Version: 0.9.1 Description: Built-in web server. diff --git a/system/extensions/yellow-language.ini b/system/extensions/yellow-language.ini @@ -4,10 +4,6 @@ Language: en LanguageLocale: en_GB LanguageDescription: English LanguageTranslator: Mark Seuffert -BlogDescription: Blog for your website. -BlogBy: by -BlogTag: Tags: -BlogMore: Read more… CoreDescription: Core functionality of your website. CoreNavigation: Main CorePagination: Page diff --git a/system/extensions/yellow-system.ini b/system/extensions/yellow-system.ini @@ -16,24 +16,24 @@ CoreContentErrorFile: page-error-(.*).md CoreLanguageFile: yellow-language.ini CoreUserFile: yellow-user.ini CoreWebsiteFile: yellow-website.log +CoreAssetLocation: /assets/ CoreMediaLocation: /media/ CoreDownloadLocation: /media/downloads/ CoreImageLocation: /media/images/ CoreThumbnailLocation: /media/thumbnails/ -CoreExtensionLocation: /media/extensions/ -CoreThemeLocation: /media/themes/ CoreMultiLanguageMode: 0 CoreDebugMode: 0 -UpdateCurrentRelease: 0.8.23 -UpdateLatestUrl: auto -UpdateLatestFile: update-latest.ini -UpdateCurrentFile: update-current.ini +UpdateCurrentRelease: 0.9 +UpdateAvailableUrl: auto +UpdateAvailableFile: update-available.ini +UpdateInstalledFile: update-installed.ini UpdateExtensionFile: extension.ini UpdateEventPending: none -UpdateEventDaily: 1703376051 +UpdateEventDaily: 1772496051 UpdateTrashTimeout: 7776660 BlogStartLocation: /write/ -BlogNewLocation: @title +BlogNewLocation: @date-@title +BlogFilePrefix: 1 BlogShortcutEntries: 0 BlogPaginationLimit: 5 EditSiteEmail: noreply @@ -52,11 +52,21 @@ EditUserHome: / EditLoginRestriction: 0 EditLoginSessionTimeout: 2592000 EditBruteForceProtection: 25 -GenerateStaticUrl: auto +GenerateStaticUrl: https://mikuli.cz GenerateStaticDirectory: public/ GenerateStaticDefaultFile: index.html GenerateStaticErrorFile: 404.html ImageUploadWidthMax: 1280 ImageUploadHeightMax: 1280 +ImageUploadJpegQuality: 80 +ImageThumbnailJpegQuality: 80 +ImageJpegExtension: auto +PreviousnextPagePrevious: 1 +PreviousnextPageNext: 1 +CoreExtensionLocation: /media/extensions/ +CoreThemeLocation: /media/themes/ +UpdateLatestUrl: auto +UpdateLatestFile: update-latest.ini +UpdateCurrentFile: update-current.ini ImageUploadJpgQuality: 80 ImageThumbnailJpgQuality: 80 diff --git a/system/extensions/yellow-website.log b/system/extensions/yellow-website.log @@ -12,3 +12,7 @@ 2023-12-22 17:46:30 info Install extension 'Swedish 0.8.43' 2023-12-22 17:46:45 info Add user 'asd' 2023-12-23 08:35:04 info Install extension 'Blog 0.8.30' +2026-03-02 21:35:59 info Uninstall extension 'Install 0.9.10' +2026-03-02 21:35:59 info Update Datenstrom Yellow 0.9, PHP 8.3.30, Built-in 8.3.30, Linux +2026-03-02 21:53:49 info Install extension 'Blog 0.9.4' +2026-03-02 22:02:06 info Install extension 'Previousnext 0.9.1' diff --git a/system/layouts/blog-start.html b/system/layouts/blog-start.html @@ -8,7 +8,7 @@ <?php foreach ($pages as $page): ?> <?php $page->set("entryClass", "entry") ?> <?php if ($page->isExisting("tag")): ?> -<?php foreach (preg_split("/\s*,\s*/", $page->get("tag")) as $tag) { $page->set("entryClass", $page->get("entryClass")." tag-".$this->yellow->lookup->normaliseArguments($tag, false)); } ?> +<?php foreach (preg_split("/\s*,\s*/", $page->get("tag")) as $tag) { $page->set("entryClass", $page->get("entryClass")." tag-".$this->yellow->lookup->normaliseClass($tag)); } ?> <?php endif ?> <div class="<?php echo $page->getHtml("entryClass") ?>"> <div class="entry-title"><h1><a href="<?php echo $page->getLocation(true) ?>"><?php echo $page->getHtml("title") ?></a></h1></div> diff --git a/system/layouts/blog.html b/system/layouts/blog.html @@ -3,7 +3,7 @@ <div class="main" role="main"> <?php $this->yellow->page->set("entryClass", "entry") ?> <?php if ($this->yellow->page->isExisting("tag")): ?> -<?php foreach (preg_split("/\s*,\s*/", $this->yellow->page->get("tag")) as $tag) { $this->yellow->page->set("entryClass", $this->yellow->page->get("entryClass")." tag-".$this->yellow->lookup->normaliseArguments($tag, false)); } ?> +<?php foreach (preg_split("/\s*,\s*/", $this->yellow->page->get("tag")) as $tag) { $this->yellow->page->set("entryClass", $this->yellow->page->get("entryClass")." tag-".$this->yellow->lookup->normaliseClass($tag)); } ?> <?php endif ?> <div class="<?php echo $this->yellow->page->getHtml("entryClass") ?>"> <div class="entry-title"><h1><?php echo $this->yellow->page->getHtml("titleContent") ?></h1></div> diff --git a/system/workers/blog.php b/system/workers/blog.php @@ -0,0 +1,252 @@ +<?php +// Blog extension, https://github.com/annaesvensson/yellow-blog + +class YellowBlog { + const VERSION = "0.9.4"; + public $yellow; // access to API + + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + $this->yellow->system->setDefault("blogStartLocation", "auto"); + $this->yellow->system->setDefault("blogNewLocation", "@title"); + $this->yellow->system->setDefault("blogFilePrefix", "1"); + $this->yellow->system->setDefault("blogShortcutEntries", "0"); + $this->yellow->system->setDefault("blogPaginationLimit", "5"); + } + + // Handle page meta data + public function onParseMetaData($page) { + if ($page->get("layout")=="blog") { + $page->set("editNewLocation", $this->yellow->system->get("blogNewLocation")); + if ($this->yellow->system->get("blogFilePrefix")) $page->set("editNewPrefix", $page->get("published")); + } + } + + // Handle page content element + public function onParseContentElement($page, $name, $text, $attributes, $type) { + $output = null; + if (substru($name, 0, 4)=="blog" && ($type=="block" || $type=="inline")) { + switch($name) { + case "blogauthors": $output = $this->getShortcutBlogauthors($page, $name, $text); break; + case "blogtags": $output = $this->getShortcutBlogtags($page, $name, $text); break; + case "blogyears": $output = $this->getShortcutBlogyears($page, $name, $text); break; + case "blogmonths": $output = $this->getShortcutBlogmonths($page, $name, $text); break; + case "blogpages": $output = $this->getShortcutBlogpages($page, $name, $text); break; + } + } + return $output; + } + + // Return blogauthors shortcut + public function getShortcutBlogauthors($page, $name, $text) { + $output = null; + list($startLocation, $shortcutEntries) = $this->yellow->toolbox->getTextArguments($text); + if (is_string_empty($startLocation)) $startLocation = $this->yellow->system->get("blogStartLocation"); + if (is_string_empty($shortcutEntries)) $shortcutEntries = $this->yellow->system->get("blogShortcutEntries"); + $blogStart = $this->getBlogStart($page, $startLocation); + if (!is_null($blogStart)) { + $pages = $this->getBlogPages($blogStart); + $page->setLastModified($pages->getModified()); + $authors = $pages->group("author", false, "count"); + if ($shortcutEntries!=0) $authors = array_slice($authors, 0, $shortcutEntries, true); + uksort($authors, "strnatcasecmp"); + $output = "<div class=\"".htmlspecialchars($name)."\">\n"; + $output .= "<ul>\n"; + foreach ($authors as $author=>$collection) { + $output .= "<li><a href=\"".$blogStart->getLocation(true).$this->yellow->lookup->normaliseArguments("author:$author")."\">"; + $output .= htmlspecialchars($author)."</a></li>\n"; + } + $output .= "</ul>\n"; + $output .= "</div>\n"; + } else { + $page->error(500, "Blogauthors '$startLocation' does not exist!"); + } + return $output; + } + + // Return blogtags shortcut + public function getShortcutBlogtags($page, $name, $text) { + $output = null; + list($startLocation, $shortcutEntries) = $this->yellow->toolbox->getTextArguments($text); + if (is_string_empty($startLocation)) $startLocation = $this->yellow->system->get("blogStartLocation"); + if (is_string_empty($shortcutEntries)) $shortcutEntries = $this->yellow->system->get("blogShortcutEntries"); + $blogStart = $this->getBlogStart($page, $startLocation); + if (!is_null($blogStart)) { + $pages = $this->getBlogPages($blogStart); + $page->setLastModified($pages->getModified()); + $tags = $pages->group("tag", false, "count"); + if ($shortcutEntries!=0) $tags = array_slice($tags, 0, $shortcutEntries, true); + uksort($tags, "strnatcasecmp"); + $output = "<div class=\"".htmlspecialchars($name)."\">\n"; + $output .= "<ul>\n"; + foreach ($tags as $tag=>$collection) { + $output .= "<li><a href=\"".$blogStart->getLocation(true).$this->yellow->lookup->normaliseArguments("tag:$tag")."\">"; + $output .= htmlspecialchars($tag)."</a></li>\n"; + } + $output .= "</ul>\n"; + $output .= "</div>\n"; + } else { + $page->error(500, "Blogtags '$startLocation' does not exist!"); + } + return $output; + } + + // Return blogyears shortcut + public function getShortcutBlogyears($page, $name, $text) { + $output = null; + list($startLocation, $shortcutEntries) = $this->yellow->toolbox->getTextArguments($text); + if (is_string_empty($startLocation)) $startLocation = $this->yellow->system->get("blogStartLocation"); + if (is_string_empty($shortcutEntries)) $shortcutEntries = $this->yellow->system->get("blogShortcutEntries"); + $blogStart = $this->getBlogStart($page, $startLocation); + if (!is_null($blogStart)) { + $pages = $this->getBlogPages($blogStart); + $page->setLastModified($pages->getModified()); + $years = $pages->group("published", false, "Y"); + if ($shortcutEntries!=0) $years = array_slice($years, 0, $shortcutEntries, true); + $output = "<div class=\"".htmlspecialchars($name)."\">\n"; + $output .= "<ul>\n"; + foreach ($years as $year=>$collection) { + $output .= "<li><a href=\"".$blogStart->getLocation(true).$this->yellow->lookup->normaliseArguments("published:$year")."\">"; + $output .= htmlspecialchars($this->yellow->language->getDateStandard($year))."</a></li>\n"; + } + $output .= "</ul>\n"; + $output .= "</div>\n"; + } else { + $page->error(500, "Blogyears '$startLocation' does not exist!"); + } + return $output; + } + + // Return blogmonths shortcut + public function getShortcutBlogmonths($page, $name, $text) { + $output = null; + list($startLocation, $shortcutEntries) = $this->yellow->toolbox->getTextArguments($text); + if (is_string_empty($startLocation)) $startLocation = $this->yellow->system->get("blogStartLocation"); + if (is_string_empty($shortcutEntries)) $shortcutEntries = $this->yellow->system->get("blogShortcutEntries"); + $blogStart = $this->getBlogStart($page, $startLocation); + if (!is_null($blogStart)) { + $pages = $this->getBlogPages($blogStart); + $page->setLastModified($pages->getModified()); + $months = $pages->group("published", false, "Y-m"); + if ($shortcutEntries!=0) $months = array_slice($months, 0, $shortcutEntries, true); + $output = "<div class=\"".htmlspecialchars($name)."\">\n"; + $output .= "<ul>\n"; + foreach ($months as $month=>$collection) { + $output .= "<li><a href=\"".$blogStart->getLocation(true).$this->yellow->lookup->normaliseArguments("published:$month")."\">"; + $output .= htmlspecialchars($this->yellow->language->getDateStandard($month))."</a></li>\n"; + } + $output .= "</ul>\n"; + $output .= "</div>\n"; + } else { + $page->error(500, "Blogmonths '$startLocation' does not exist!"); + } + return $output; + } + + // Return blogpages shortcut + public function getShortcutBlogpages($page, $name, $text) { + $output = null; + list($startLocation, $shortcutEntries, $filterTag) = $this->yellow->toolbox->getTextArguments($text); + if (is_string_empty($startLocation)) $startLocation = $this->yellow->system->get("blogStartLocation"); + if (is_string_empty($shortcutEntries)) $shortcutEntries = $this->yellow->system->get("blogShortcutEntries"); + $blogStart = $this->getBlogStart($page, $startLocation); + if (!is_null($blogStart)) { + $pages = $this->getBlogPages($blogStart)->remove($page); + $page->setLastModified($pages->getModified()); + if (!is_string_empty($filterTag)) $pages->filter("tag", $filterTag); + $pages->sort("published", false); + if ($shortcutEntries!=0) $pages->limit($shortcutEntries); + $output = "<div class=\"".htmlspecialchars($name)."\">\n"; + $output .= "<ul>\n"; + foreach ($pages as $pageBlog) { + $output .= "<li><a".($pageBlog->isExisting("tag") ? " class=\"".$this->getClass($pageBlog)."\"" : ""); + $output .=" href=\"".$pageBlog->getLocation(true)."\">".$pageBlog->getHtml("title")."</a></li>\n"; + } + $output .= "</ul>\n"; + $output .= "</div>\n"; + } else { + $page->error(500, "Blogpages '$startLocation' does not exist!"); + } + return $output; + } + + // Handle page layout + public function onParsePageLayout($page, $name) { + if ($name=="blog-start") { + $pages = $this->getBlogPages($page); + $pagesFilter = array(); + if ($page->isRequest("tag")) { + $pages->filter("tag", $page->getRequest("tag")); + array_push($pagesFilter, $pages->getFilter()); + } + if ($page->isRequest("author")) { + $pages->filter("author", $page->getRequest("author")); + array_push($pagesFilter, $pages->getFilter()); + } + if ($page->isRequest("published")) { + $pages->filter("published", $page->getRequest("published"), false); + array_push($pagesFilter, $this->yellow->language->getDateStandard($pages->getFilter())); + } + $pages->sort("published", false); + if (!is_array_empty($pagesFilter)) { + $text = implode(" ", $pagesFilter); + $page->set("titleHeader", $text." - ".$page->get("sitename")); + $page->set("titleContent", $page->get("title").": ".$text); + $page->set("title", $page->get("title").": ".$text); + $page->set("blogWithFilter", true); + } + $page->setPages("blog", $pages); + $page->setLastModified($pages->getModified()); + $page->setHeader("Cache-Control", "max-age=60"); + } + if ($name=="blog") { + $blogStartLocation = $this->yellow->system->get("blogStartLocation"); + if ($blogStartLocation=="auto") { + $blogStart = $page->getParent(); + } else { + $blogStart = $this->yellow->content->find($blogStartLocation); + } + $page->setPage("blogStart", $blogStart); + } + } + + // Return blog start page, null if not found + public function getBlogStart($page, $blogStartLocation) { + if ($blogStartLocation=="auto") { + $blogStart = null; + foreach ($this->yellow->content->top(true, false) as $pageTop) { + if ($pageTop->get("layout")=="blog-start") { + $blogStart = $pageTop; + break; + } + } + if ($page->get("layout")=="blog-start") $blogStart = $page; + } else { + $blogStart = $this->yellow->content->find($blogStartLocation); + } + return $blogStart; + } + + // Return blog pages for page + public function getBlogPages($page) { + if ($this->yellow->system->get("blogStartLocation")=="auto") { + $pages = $page->getChildren(); + } else { + $pages = $this->yellow->content->index(); + } + $pages->filter("layout", "blog"); + return $pages; + } + + // Return class for page + public function getClass($page) { + $class = ""; + if ($page->isExisting("tag")) { + foreach (preg_split("/\s*,\s*/", $page->get("tag")) as $tag) { + $class .= " tag-".$this->yellow->lookup->normaliseClass($tag); + } + } + return trim($class); + } +} diff --git a/system/workers/install-blog.bin b/system/workers/install-blog.bin Binary files differ. diff --git a/system/workers/install-language.bin b/system/workers/install-language.bin Binary files differ. diff --git a/system/workers/install-wiki.bin b/system/workers/install-wiki.bin Binary files differ. diff --git a/system/workers/install.php b/system/workers/install.php @@ -1,581 +0,0 @@ -<?php -// Install extension, https://github.com/annaesvensson/yellow-install - -class YellowInstall { - const VERSION = "0.9.10"; - const PRIORITY = "1"; - public $yellow; // access to API - - // Handle initialisation - public function onLoad($yellow) { - $this->yellow = $yellow; - } - - // Handle request - public function onRequest($scheme, $address, $base, $location, $fileName) { - return $this->processRequestInstall($scheme, $address, $base, $location, $fileName); - } - - // Handle command - public function onCommand($command, $text) { - return $this->processCommandInstall($command, $text); - } - - // Process request to install website - public function processRequestInstall($scheme, $address, $base, $location, $fileName) { - $statusCode = 0; - if ($this->yellow->lookup->isContentFile($fileName) || is_string_empty($fileName)) { - if ($this->yellow->system->get("updateCurrentRelease")=="none") { - $this->checkServerRequirements(); - $author = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $this->yellow->page->getRequest("author"))); - $email = trim($this->yellow->page->getRequest("email")); - $password = trim($this->yellow->page->getRequest("password")); - $language = trim($this->yellow->page->getRequest("language")); - $extension = trim($this->yellow->page->getRequest("extension")); - $status = trim($this->yellow->page->getRequest("status")); - $statusCode = $this->updateLog(); - $statusCode = max($statusCode, $this->updateLanguages("small")); - $errorMessage = $this->yellow->page->errorMessage; - $this->yellow->content->pages["root/"] = array(); - $this->yellow->page = new YellowPage($this->yellow); - $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->parseMeta($this->getRawDataInstall(), $statusCode, $errorMessage); - $this->yellow->page->parseContent(); - $this->yellow->page->parsePage(); - if ($status=="install") $status = $this->updateExtensions("small", $extension)==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateUser($email, $password, $author, $language)==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateAuthentication($scheme, $address, $base, $email)==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "installHome", "/")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "installAbout", "/about/")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "installDefault", "/shared/page-new-default")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "installWiki", "/shared/page-new-wiki")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "installBlog", "/shared/page-new-blog")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateContent($language, "coreError404", "/shared/page-error-404")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateSettings()==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->removeInstall()==200 ? "done" : "error"; - } else { - $status = $this->removeInstall(true)==200 ? "done" : "error"; - } - if ($status=="done") { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, "/"); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $statusCode = $this->yellow->sendData($this->yellow->page->statusCode, $this->yellow->page->headerData, $this->yellow->page->outputData); - } - } - return $statusCode; - } - - // Process command to install website - public function processCommandInstall($command, $text) { - $statusCode = 0; - if ($this->yellow->system->get("updateCurrentRelease")=="none") { - $this->checkCommandRequirements(); - list($installation, $option) = $this->yellow->toolbox->getTextArguments($text); - if (is_string_empty($command)) { - $statusCode = 200; - echo "Datenstrom Yellow is for people who make small websites. https://datenstrom.se/yellow/\n"; - echo "Syntax: php yellow.php\n"; - echo " php yellow.php about [extension]\n"; - echo " php yellow.php serve [url]\n"; - echo " php yellow.php skip installation [option]\n"; - } elseif ($command=="about" || $command=="serve") { - $statusCode = 0; - } elseif ($command=="skip" && $installation=="installation") { - $statusCode = $this->updateLog(); - if ($statusCode==200) $statusCode = $this->updateLanguages($option); - if ($statusCode==200) $statusCode = $this->updateExtensions($option, ""); - if ($statusCode==200) $statusCode = $this->updateSettings(true); - if ($statusCode==200) $statusCode = $this->removeInstall(); - if ($statusCode>=400) { - echo "ERROR installing files: ".$this->yellow->page->errorMessage."\n"; - echo "The installation has not been completed. Please run command again.\n"; - } else { - $extensionsCount = $this->getExtensionsCount(); - echo "Yellow $command: $extensionsCount extension".($extensionsCount!=1 ? "s" : "").", 0 errors\n"; - } - } else { - $statusCode = 500; - echo "The installation has not been completed. Please type 'php yellow.php serve' or 'php yellow.php skip installation`.\n"; - } - } else { - $statusCode = $this->removeInstall(true); - if ($statusCode==200) $statusCode = 0; - if ($statusCode>=400) { - echo "ERROR installing files: ".$this->yellow->page->errorMessage."\n"; - echo "Detected ZIP files, 0 extensions installed. Please run command again.\n"; - } - } - return $statusCode; - } - - // Update log file - public function updateLog() { - $statusCode = 200; - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreWebsiteFile"); - if (!is_file($fileName)) { - list($name, $version, $os) = $this->yellow->toolbox->detectServerInformation(); - $product = "Datenstrom Yellow ".YellowCore::RELEASE; - $this->yellow->toolbox->log("info", "Install $product, PHP ".PHP_VERSION.", $name $version, $os"); - foreach ($this->yellow->extension->data as $key=>$value) { - if ($key=="install") continue; - $this->yellow->toolbox->log("info", "Install extension '".ucfirst($key)." $value[version]'"); - } - if (!is_file($fileName)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - return $statusCode; - } - - // Update languages - public function updateLanguages($option) { - $statusCode = 200; - $path = $this->yellow->system->get("coreWorkerDirectory")."install-language.bin"; - $zip = new ZipArchive(); - if ($zip->open($path)===true) { - $pathBase = ""; - if (preg_match("#^(.*\/).*?$#", $zip->getNameIndex(0), $matches)) $pathBase = $matches[1]; - $fileData = $zip->getFromName($pathBase.$this->yellow->system->get("updateExtensionFile")); - foreach ($this->getLanguageExtensionsRequired($fileData, $option) as $extension) { - $fileDataPhp = $zip->getFromName($pathBase."translations/$extension/$extension.php"); - $fileDataIni = $zip->getFromName($pathBase."translations/$extension/extension.ini"); - $statusCode = max($statusCode, $this->updateLanguageArchive($fileDataPhp, $fileDataIni, $pathBase, "install")); - } - $this->yellow->extension->load($this->yellow->system->get("coreWorkerDirectory")); - $this->yellow->language->load($this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreLanguageFile")); - $zip->close(); - } else { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't open file '$path'!"); - } - return $statusCode; - } - - // Update language archive - public function updateLanguageArchive($fileDataPhp, $fileDataIni, $pathBase, $action) { - $statusCode = 200; - if ($this->yellow->extension->isExisting("update")) { - $settings = $this->yellow->toolbox->getTextSettings($fileDataIni, ""); - $extension = lcfirst($settings->get("extension")); - $version = $settings->get("version"); - $modified = strtotime($settings->get("published")); - $fileNamePhp = $this->yellow->system->get("coreWorkerDirectory").$extension.".php"; - if (!is_string_empty($extension) && !is_string_empty($version) && !is_file($fileNamePhp)) { - $statusCode = max($statusCode, $this->yellow->extension->get("update")->updateExtensionSettings($extension, $action, $fileDataIni)); - $statusCode = max($statusCode, $this->yellow->extension->get("update")->updateExtensionFile( - $fileNamePhp, $fileDataPhp, $modified, 0, 0, "create", $extension)); - $this->yellow->toolbox->log($statusCode==200 ? "info" : "error", ucfirst($action)." extension '".ucfirst($extension)." $version'"); - } - } - return $statusCode; - } - - // Update extensions - public function updateExtensions($option, $extension) { - $statusCode = 200; - if ($this->yellow->extension->isExisting("update")) { - if (!is_string_empty($option)) { - if ($option=="medium" || $option=="large") { - $path = $this->yellow->system->get("coreExtensionDirectory"); - $fileData = $this->yellow->toolbox->readFile($path.$this->yellow->system->get("updateAvailableFile")); - $settings = $this->yellow->toolbox->getTextSettings($fileData, "extension"); - $extensions = $this->getAvailableExtensionsRequired($settings, $option); - $statusCode = $this->downloadExtensionsAvailable($settings, $extensions); - $path = $this->yellow->system->get("coreWorkerDirectory"); - foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^install-.*\.bin$/", true, false) as $entry) { - if (basename($entry)=="install-language.bin") continue; - if (preg_match("/^install-(.*?)\.bin/", basename($entry), $matches) && !in_array($matches[1], $extensions)) continue; - $statusCode = max($statusCode, $this->yellow->extension->get("update")->updateExtensionArchive($entry, "install")); - } - } - if (!($option=="small" || $option=="medium" || $option=="large")) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Option '$option' not supported!"); - } - } - if (!is_string_empty($extension)) { - $path = $this->yellow->system->get("coreWorkerDirectory")."install-".$extension.".bin"; - if (is_file($path)) { - $statusCode = $this->yellow->extension->get("update")->updateExtensionArchive($path, "install"); - } - } - } - return $statusCode; - } - - // Update user - public function updateUser($email, $password, $name, $language) { - $statusCode = 200; - if ($this->yellow->extension->isExisting("edit") && !is_string_empty($email) && !is_string_empty($password)) { - if (is_string_empty($name)) $name = $this->yellow->system->get("sitename"); - $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); - $settings = array( - "name" => $name, - "description" => $this->yellow->language->getText("editUserDescription", $language), - "language" => $language, - "access" => "create, edit, delete, restore, upload, configure, update", - "home" => "/", - "hash" => $this->yellow->extension->get("edit")->response->createHash($password), - "stamp" => $this->yellow->extension->get("edit")->response->createStamp(), - "pending" => "none", - "failed" => "0", - "modified" => date("Y-m-d H:i:s", time()), - "status" => "active"); - if (!$this->yellow->user->save($fileNameUser, $email, $settings)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileNameUser'!"); - } - $this->yellow->toolbox->log($statusCode==200 ? "info" : "error", "Add user '".strtok($name, " ")."'"); - } - return $statusCode; - } - - // Update authentication - public function updateAuthentication($scheme, $address, $base, $email) { - if ($this->yellow->extension->isExisting("edit") && $this->yellow->user->isExisting($email)) { - $base = rtrim($base.$this->yellow->system->get("editLocation"), "/"); - $this->yellow->extension->get("edit")->response->createCookies($scheme, $address, $base, $email); - } - return 200; - } - - // Update content - public function updateContent($language, $name, $location) { - $statusCode = 200; - $fileName = $this->yellow->lookup->findFileFromContentLocation($location); - $fileData = str_replace("\r\n", "\n", $this->yellow->toolbox->readFile($fileName)); - if (!is_string_empty($fileData) && $language!="en") { - $titleOld = "Title: ".$this->yellow->language->getText("{$name}Title", "en")."\n"; - $titleNew = "Title: ".$this->yellow->language->getText("{$name}Title", $language)."\n"; - $fileData = str_replace($titleOld, $titleNew, $fileData); - $textOld = str_replace("\\n", "\n", $this->yellow->language->getText("{$name}Text", "en")); - $textNew = str_replace("\\n", "\n", $this->yellow->language->getText("{$name}Text", $language)); - $fileData = str_replace($textOld, $textNew, $fileData); - if (!$this->yellow->toolbox->writeFile($fileName, $fileData)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - return $statusCode; - } - - // Update settings - public function updateSettings($skipInstallation = false) { - $statusCode = 200; - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreSystemFile"); - if (!$this->yellow->system->save($fileName, $this->getSystemSettings($skipInstallation))) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - $language = $this->yellow->system->get("language"); - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreLanguageFile"); - $fileData = $this->yellow->toolbox->readFile($fileName); - if (strposu($fileData, "Language:")===false) { - if (!is_string_empty($fileData)) $fileData .= "\n"; - $fileData .= "Language: $language\n"; - $fileData .= "media/images/photo.jpg: ".$this->yellow->language->getText("installExampleImage", $language)."\n"; - if (!$this->yellow->toolbox->writeFile($fileName, $fileData)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - return $statusCode; - } - - // Remove files used by installation - public function removeInstall($log = false) { - $statusCode = 200; - if (function_exists("opcache_reset")) opcache_reset(); - $this->yellow->toolbox->deleteFile("license.md"); - $fileName = $this->yellow->system->get("coreWorkerDirectory")."install.php"; - if (!$this->yellow->toolbox->deleteFile($fileName)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); - } - $path = $this->yellow->system->get("coreWorkerDirectory"); - foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^install-.*\.bin$/", true, false) as $entry) { - if (!$this->yellow->toolbox->deleteFile($entry)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); - } - } - if ($statusCode==200) unset($this->yellow->extension->data["install"]); - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("updateInstalledFile"); - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataNew = $this->yellow->toolbox->unsetTextSettings($fileData, "extension", "install"); - if ($statusCode==200 && !$this->yellow->toolbox->writeFile($fileName, $fileDataNew)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - if ($log) $this->yellow->toolbox->log($statusCode==200 ? "info" : "error", "Uninstall extension 'Install ".YellowInstall::VERSION."'"); - return $statusCode; - } - - // Check web server requirements - public function checkServerRequirements() { - if ($this->yellow->system->get("coreDebugMode")>=1) { - list($name, $version, $os) = $this->yellow->toolbox->detectServerInformation(); - echo "YellowInstall::checkServerRequirements for $name $version, $os<br />\n"; - } - if (!$this->checkServerComplete()) $this->yellow->exitFatalError("Datenstrom Yellow requires complete upload!"); - if (!$this->checkServerWrite()) $this->yellow->exitFatalError("Datenstrom Yellow requires write access!"); - if (!$this->checkServerHtaccess()) $this->yellow->exitFatalError("Datenstrom Yellow requires htaccess file!"); - if (!$this->checkServerRewrite()) $this->yellow->exitFatalError("Datenstrom Yellow requires rewrite support!"); - } - - // Check command line requirements - public function checkCommandRequirements() { - if ($this->yellow->system->get("coreDebugMode")>=1) { - list($name, $version, $os) = $this->yellow->toolbox->detectServerInformation(); - echo "YellowInstall::checkCommandRequirements for $name $version, $os<br />\n"; - } - if (!$this->checkServerComplete()) $this->yellow->exitFatalError("Datenstrom Yellow requires complete upload!"); - if (!$this->checkServerWrite()) $this->yellow->exitFatalError("Datenstrom Yellow requires write access!"); - } - - // Check web server complete upload - public function checkServerComplete() { - $complete = true; - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("updateInstalledFile"); - $fileData = $this->yellow->toolbox->readFile($fileName); - $settings = $this->yellow->toolbox->getTextSettings($fileData, "extension"); - $fileNames = array($fileName); - foreach ($settings as $extension=>$block) { - foreach ($block as $key=>$value) { - if (strposu($key, "/")) { - list($entry, $flags) = $this->yellow->toolbox->getTextList($value, ",", 2); - if (!preg_match("/create/i", $flags)) continue; - if (preg_match("/delete/i", $flags)) continue; - if (preg_match("/additional/i", $flags)) continue; - array_push($fileNames, $key); - } - } - } - foreach ($fileNames as $fileName) { - if (!is_file($fileName) || filesize($fileName)==0) { - $complete = false; - if ($this->yellow->system->get("coreDebugMode")>=1) { - echo "YellowInstall::checkServerComplete detected missing file:$fileName<br />\n"; - } - } - } - return $complete; - } - - // Check web server write access - public function checkServerWrite() { - $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreSystemFile"); - return $this->yellow->system->save($fileName, array()); - } - - // Check web server htaccess file - public function checkServerHtaccess() { - list($name) = $this->yellow->toolbox->detectServerInformation(); - return strtoloweru($name)!="apache" || is_file(".htaccess"); - } - - // Check web server rewrite support - public function checkServerRewrite() { - $rewrite = true; - if (!$this->isServerBuiltin()) { - $curlHandle = curl_init(); - list($scheme, $address, $base) = $this->yellow->lookup->getRequestInformation(); - $location = $this->yellow->system->get("coreAssetLocation").$this->yellow->lookup->normaliseName($this->yellow->system->get("theme")).".css"; - $url = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - curl_setopt($curlHandle, CURLOPT_URL, $url); - curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; YellowInstall/".YellowInstall::VERSION).")"; - curl_setopt($curlHandle, CURLOPT_NOBODY, 1); - curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); - curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, false); - curl_exec($curlHandle); - $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - if (PHP_VERSION_ID<80000) curl_close($curlHandle); - if ($statusCode!=200) { - $rewrite = false; - if ($this->yellow->system->get("coreDebugMode")>=1 && !$rewrite) { - echo "YellowInstall::checkServerRewrite detected failed url:$url<br />\n"; - } - } - } - return $rewrite; - } - - // Download available extension files - public function downloadExtensionsAvailable($settings, $extensions) { - $statusCode = 200; - if ($this->yellow->extension->isExisting("update")) { - $path = $this->yellow->system->get("coreWorkerDirectory"); - $extensionsNow = 0; - $extensionsTotal = count($extensions); - $curlHandle = curl_init(); - foreach ($extensions as $extension) { - echo "\rDownloading available extensions ".$this->getProgressPercent(++$extensionsNow, $extensionsTotal, 5, 95)."%... "; - $fileName = $path."install-".$this->yellow->lookup->normaliseName($extension, true, false, true).".bin"; - if (is_file($fileName)) continue; - $url = $settings[$extension]->get("downloadUrl"); - curl_setopt($curlHandle, CURLOPT_URL, $this->yellow->extension->get("update")->getExtensionDownloadUrl($url)); - curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; YellowInstall/".YellowInstall::VERSION).")"; - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); - $fileData = curl_exec($curlHandle); - $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - $redirectUrl = ($statusCode>=300 && $statusCode<=399) ? curl_getinfo($curlHandle, CURLINFO_REDIRECT_URL) : ""; - if ($statusCode==0) { - $statusCode = 450; - $this->yellow->page->error($statusCode, "Can't connect to the update server!"); - } - if ($statusCode!=450 && $statusCode!=200) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't download file '$url'!"); - } - if ($statusCode==200 && !$this->yellow->toolbox->writeFile($fileName, $fileData)) { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - if ($this->yellow->system->get("coreDebugMode")>=2 && !is_string_empty($redirectUrl)) { - echo "YellowInstall::downloadExtensionsAvailable redirected to url:$redirectUrl<br />\n"; - } - if ($this->yellow->system->get("coreDebugMode")>=2) { - echo "YellowInstall::downloadExtensionsAvailable status:$statusCode url:$url<br />\n"; - } - if ($statusCode!=200) break; - } - if (PHP_VERSION_ID<80000) curl_close($curlHandle); - echo "\rDownloading available extensions 100%... done\n"; - } - return $statusCode; - } - - // Return available extensions required - public function getAvailableExtensionsRequired($settings, $option) { - $extensions = array(); - if ($option=="medium") { - $text = "help highlight search toc"; - $extensions = array_unique(array_filter($this->yellow->toolbox->getTextArguments($text), "strlen")); - } elseif ($option=="large") { - foreach ($settings as $key=>$value) { - if (preg_match("/language/i", $value->get("tag"))) continue; - array_push($extensions, strtoloweru($key)); - } - } - return $extensions; - } - - // Return language extensions required - public function getLanguageExtensionsRequired($fileData, $option) { - $extensions = array(); - $languages = array(); - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (!is_string_empty($matches[1]) && !is_string_empty($matches[2]) && strposu($matches[1], "/")) { - $extension = basename($matches[1]); - $extension = $this->yellow->lookup->normaliseName($extension, true, true); - list($entry, $flags) = $this->yellow->toolbox->getTextList($matches[2], ",", 2); - $arguments = preg_split("/\s*,\s*/", trim($flags)); - $language = array_pop($arguments); - if (preg_match("/^(.*)\.php$/", basename($entry))) { - $languages[$language] = $extension; - } - } - } - } - if ($option=="large") { - foreach ($languages as $language=>$extension) { - array_push($extensions, $extension); - } - } else { - foreach ($this->getSystemLanguages("en, de, sv") as $language) { - if (isset($languages[$language])) array_push($extensions, $languages[$language]); - } - $extensions = array_slice($extensions, 0, 3); - } - return $extensions; - } - - // Return extensions installed - public function getExtensionsCount() { - $fileNameCurrent = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("updateInstalledFile"); - $fileData = $this->yellow->toolbox->readFile($fileNameCurrent); - $settings = $this->yellow->toolbox->getTextSettings($fileData, "extension"); - return count($settings); - } - - // Return system languages - public function getSystemLanguages($languagesDefault) { - $languages = array(); - foreach (preg_split("/\s*,\s*/", $this->yellow->toolbox->getServer("HTTP_ACCEPT_LANGUAGE")) as $string) { - list($language, $dummy) = $this->yellow->toolbox->getTextList($string, ";", 2); - if (!is_string_empty($language)) array_push($languages, $language); - } - foreach (preg_split("/\s*,\s*/", $languagesDefault) as $language) { - if (!is_string_empty($language)) array_push($languages, $language); - } - return array_unique($languages); - } - - // Return system settings - public function getSystemSettings($skipInstallation) { - $settings = array(); - foreach ($_REQUEST as $key=>$value) { - if (!$this->yellow->system->isExisting($key)) continue; - if ($key=="password" || $key=="status") continue; - $settings[$key] = trim($value); - } - if ($this->yellow->system->get("sitename")=="Datenstrom Yellow") $settings["sitename"] = $this->yellow->toolbox->detectServerSitename(); - if ($this->yellow->system->get("generateStaticUrl")=="auto" && getenv("URL")!==false) $settings["generateStaticUrl"] = getenv("URL"); - if ($this->yellow->system->get("generateStaticUrl")=="auto" && $skipInstallation) $settings["generateStaticUrl"] = "http://localhost:8000/"; - if ($this->yellow->system->get("coreTimezone")=="UTC") $settings["coreTimezone"] = $this->yellow->toolbox->detectServerTimezone(); - if ($this->yellow->system->get("updateEventPending")=="none") { - $settings["updateEventPending"] = "website/install"; - } else { - $themeStandard = ",".$this->yellow->system->get("theme")."/install"; - $settings["updateEventPending"] = $this->yellow->system->get("updateEventPending").$themeStandard; - } - $settings["updateCurrentRelease"] = YellowCore::RELEASE; - return $settings; - } - - // Return raw data for install page - public function getRawDataInstall() { - $languages = $this->yellow->toolbox->enumerate("language"); - $language = $this->yellow->toolbox->detectBrowserLanguage($languages, $this->yellow->system->get("language")); - $this->yellow->language->set($language); - $rawData = "---\nTitle:".$this->yellow->language->getText("installTitle")."\nLanguage:$language\nNavigation:navigation\nHeader:none\nFooter:none\nSidebar:none\n---\n"; - $rawData .= "<form class=\"install-form\" action=\"".$this->yellow->page->getLocation(true)."\" method=\"post\">\n"; - $rawData .= "<p><label for=\"author\">".$this->yellow->language->getText("editSignupName")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"author\" id=\"author\" value=\"\" /></p>\n"; - $rawData .= "<p><label for=\"email\">".$this->yellow->language->getText("editSignupEmail")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"email\" id=\"email\" value=\"\" /></p>\n"; - $rawData .= "<p><label for=\"password\">".$this->yellow->language->getText("editSignupPassword")."</label><br /><input class=\"form-control\" type=\"password\" maxlength=\"64\" name=\"password\" id=\"password\" value=\"\" /></p>\n"; - $rawData .= "<p>".$this->yellow->language->getText("installLanguage")."</p>\n<p>"; - foreach ($languages as $language) { - $checked = $language==$this->yellow->language->language ? " checked=\"checked\"" : ""; - $rawData .= "<label for=\"{$language}-language\"><input type=\"radio\" name=\"language\" id=\"{$language}-language\" value=\"$language\"$checked> ".$this->yellow->language->getTextHtml("languageDescription", $language)."</label><br />"; - } - $rawData .= "</p>\n"; - $rawData .= "<p>".$this->yellow->language->getText("installExtension")."</p>\n<p>"; - foreach (array("website", "wiki", "blog") as $extension) { - $checked = $extension=="website" ? " checked=\"checked\"" : ""; - $rawData .= "<label for=\"{$extension}-extension\"><input type=\"radio\" name=\"extension\" id=\"{$extension}-extension\" value=\"$extension\"$checked> ".$this->yellow->language->getTextHtml("installExtension".ucfirst($extension))."</label><br />"; - } - $rawData .= "</p>\n"; - $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->language->getText("installButton")."\" />\n"; - $rawData .= "<input type=\"hidden\" name=\"status\" value=\"install\" />\n"; - $rawData .= "</form>\n"; - return $rawData; - } - - // Return progress in percent - public function getProgressPercent($now, $total, $increments, $max) { - $max = intval($max/$increments) * $increments; - $percent = intval(($max/$total) * $now); - if ($increments>1) $percent = intval($percent/$increments) * $increments; - return min($max, $percent); - } - - // Check if running built-in web server - public function isServerBuiltin() { - list($name) = $this->yellow->toolbox->detectServerInformation(); - return strtoloweru($name)=="built-in"; - } -} diff --git a/system/workers/previousnext.php b/system/workers/previousnext.php @@ -0,0 +1,67 @@ +<?php +// Previousnext extension, https://github.com/annaesvensson/yellow-previousnext + +class YellowPreviousnext { + const VERSION = "0.9.1"; + public $yellow; // access to API + + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + $this->yellow->system->setDefault("previousnextPagePrevious", "1"); + $this->yellow->system->setDefault("previousnextPageNext", "1"); + } + + // Handle page content element + public function onParseContentElement($page, $name, $text, $attributes, $type) { + $output = null; + if ($name=="previousnext" && ($type=="block" || $type=="inline")) { + $pages = $this->getRelatedPages($page); + $page->setLastModified($pages->getModified()); + $pagePrevious = $pageNext = null; + if ($this->yellow->system->get("previousnextPagePrevious")) $pagePrevious = $pages->getPagePrevious($page); + if ($this->yellow->system->get("previousnextPageNext")) $pageNext = $pages->getPageNext($page); + if ($pagePrevious!=null || $pageNext!=null) { + $output = "<div class=\"previousnext\" role=\"navigation\" aria-label=\"".$this->yellow->language->getTextHtml("previousnextNavigation")."\">\n"; + $output .= "<p>"; + if ($pagePrevious!=null) { + $text = preg_replace("/@title/i", $pagePrevious->get("title"), $this->yellow->language->getText("previousnextPagePrevious")); + $output .= "<a class=\"previous\" href=\"".$pagePrevious->getLocation(true)."\">".htmlspecialchars($text)."</a>"; + } + if ($pageNext!=null) { + if ($pagePrevious) $output .= " "; + $text = preg_replace("/@title/i", $pageNext->get("title"), $this->yellow->language->getText("previousnextPageNext")); + $output .= "<a class=\"next\" href=\"".$pageNext->getLocation(true)."\">".htmlspecialchars($text)."</a>"; + } + $output .= "</p>\n"; + $output .="</div>\n"; + } + } + return $output; + } + + // Handle page extra data + public function onParsePageExtra($page, $name) { + $output = null; + if ($name=="previousnext" || $name=="link") { + $output = $this->onParseContentElement($page, "previousnext", "", "", "block"); + } + return $output; + } + + // Return related pages + public function getRelatedPages($page) { + switch ($page->get("layout")) { + case "blog": if ($this->yellow->system->get("blogStartLocation")=="auto") { + $pages = $page->getSiblings(); + } else { + $pages = $this->yellow->content->index(); + } + $pages->filter("layout", "blog")->sort("published", true); + break; + case "blog-start": $pages = $this->yellow->content->clean(); break; + default: $pages = $page->getSiblings(); + } + return $pages; + } +}