├── .gitignore ├── .htaccess ├── assets ├── images │ └── logo.png └── styles │ └── styles.css ├── content ├── 01-about-us │ └── about-us.txt ├── 02-projects │ ├── 01-project-a │ │ └── project.txt │ ├── 02-project-b │ │ └── project.txt │ ├── 03-project-c │ │ └── project.txt │ └── projects.txt ├── 03-contact │ └── contact.txt ├── error │ └── error.txt ├── home │ └── home.txt └── site.txt ├── index.php ├── kirby ├── changelog.mdown ├── defaults.php ├── lib │ ├── cache.php │ ├── files.php │ ├── helpers.php │ ├── kirby.php │ ├── load.php │ ├── obj.php │ ├── pages.php │ ├── pagination.php │ ├── site.php │ ├── template.php │ ├── uri.php │ └── variables.php ├── modals │ ├── mbstring.php │ └── troubleshoot.php ├── parsers │ ├── defaults.php │ ├── kirbytext.php │ ├── markdown.extra.php │ ├── markdown.php │ ├── smartypants.php │ └── yaml.php └── system.php ├── license.mdown ├── readme.mdown └── site ├── cache └── index.html ├── config └── config.php ├── plugins └── index.html ├── snippets ├── footer.php ├── header.php ├── menu.php └── submenu.php └── templates └── default.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | panel/ 3 | site/panel 4 | docs -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | # Kirby .htaccess 2 | 3 | # rewrite rules 4 | 5 | 6 | # enable awesome urls. i.e.: 7 | # http://yourdomain.com/about-us/team 8 | RewriteEngine on 9 | 10 | # make sure to set the RewriteBase correctly 11 | # if you are running the site in a subfolder. 12 | # Otherwise links or the entire site will break. 13 | # 14 | # If your homepage is http://yourdomain.com/mysite 15 | # Set the RewriteBase to: 16 | # 17 | # RewriteBase /mysite 18 | # 19 | RewriteBase / 20 | 21 | # block text files in the content folder from being accessed directly 22 | RewriteRule ^content/(.*)\.(txt|md|mdown)$ error [R=301,L] 23 | 24 | # block all files in the site folder from being accessed directly 25 | RewriteRule ^site/(.*) error [R=301,L] 26 | 27 | # block all files in the kirby folder from being accessed directly 28 | RewriteRule ^kirby/(.*) error [R=301,L] 29 | 30 | # leave robots.txt alone for search engines 31 | RewriteRule ^robots.txt robots.txt [L] 32 | 33 | # make panel links work 34 | RewriteCond %{REQUEST_FILENAME} !-f 35 | RewriteCond %{REQUEST_FILENAME} !-d 36 | RewriteRule ^panel/(.*) panel/index.php [L] 37 | 38 | # make site links work 39 | RewriteCond %{REQUEST_FILENAME} !-f 40 | RewriteCond %{REQUEST_FILENAME} !-d 41 | RewriteRule ^(.*) index.php [L] 42 | 43 | 44 | 45 | 46 | # Additional recommended values 47 | # Remove comments for those you want to use. 48 | # 49 | # AddDefaultCharset UTF-8 50 | # 51 | # php_flag short_open_tag on 52 | -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getkirby-v1/starterkit/d547c0abcdf9e100abc55972243b7d83a783d867/assets/images/logo.png -------------------------------------------------------------------------------- /assets/styles/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | html { 6 | height: 101%; 7 | text-rendering: optimizeLegibility; 8 | -webkit-font-smoothing: antialiased; 9 | } 10 | body { 11 | font-family: Helvetica, Arial, sans-serif; 12 | font-size: 16px; 13 | line-height: 26px; 14 | background: #fff; 15 | color: #777; 16 | width: 450px; 17 | margin: 50px auto; 18 | } 19 | a { 20 | color: #222; 21 | text-decoration: none; 22 | } 23 | a, 24 | a:hover, 25 | a:active, 26 | a:visited { 27 | outline: 0px; 28 | } 29 | a:hover { 30 | color: red; 31 | } 32 | img { 33 | outline: 0; 34 | border: 0; 35 | } 36 | h1 { 37 | font-size: 28px; 38 | line-height: 40px; 39 | color: #222; 40 | margin-bottom: 20px; 41 | } 42 | h2 { 43 | font-size: 28px; 44 | font-weight: normal; 45 | color: red; 46 | margin-bottom: 21px; 47 | } 48 | h3 { 49 | font-weight: normal; 50 | font-size: 20px; 51 | color: #222; 52 | margin-bottom: 21px; 53 | } 54 | h4 { 55 | color: red; 56 | font-weight: normal; 57 | } 58 | p { 59 | margin-bottom: 20px; 60 | } 61 | nav { 62 | overflow: hidden; 63 | border-top: 1px solid red; 64 | border-bottom: 1px solid red; 65 | padding: 10px 0; 66 | } 67 | nav li { 68 | list-style: none; 69 | margin-right: 20px; 70 | font-weight: bold; 71 | float: left; 72 | } 73 | nav li a.active { 74 | color: red; 75 | } 76 | nav.submenu { 77 | border-top: none; 78 | margin-bottom: 20px; 79 | } 80 | nav.submenu a { 81 | font-weight: normal; 82 | } 83 | section.content { 84 | margin-top: 40px; 85 | } 86 | footer { 87 | padding-top: 20px; 88 | margin-top: 20px; 89 | border-top: 1px solid red; 90 | font-size: 13px; 91 | color: #222; 92 | } 93 | footer a { 94 | color: red; 95 | } 96 | footer a:hover { 97 | color: #222; 98 | } -------------------------------------------------------------------------------- /content/01-about-us/about-us.txt: -------------------------------------------------------------------------------- 1 | Title: About 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | -------------------------------------------------------------------------------- /content/02-projects/01-project-a/project.txt: -------------------------------------------------------------------------------- 1 | Title: Project A 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | 5 | -------------------------------------------------------------------------------- /content/02-projects/02-project-b/project.txt: -------------------------------------------------------------------------------- 1 | Title: Project B 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | 5 | -------------------------------------------------------------------------------- /content/02-projects/03-project-c/project.txt: -------------------------------------------------------------------------------- 1 | Title: Project C 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | 5 | -------------------------------------------------------------------------------- /content/02-projects/projects.txt: -------------------------------------------------------------------------------- 1 | Title: Projects 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | 5 | -------------------------------------------------------------------------------- /content/03-contact/contact.txt: -------------------------------------------------------------------------------- 1 | Title: Contact 2 | ---- 3 | Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, 4 | 5 | -------------------------------------------------------------------------------- /content/error/error.txt: -------------------------------------------------------------------------------- 1 | Title: Error 2 | ---- 3 | Text: The page has not been found -------------------------------------------------------------------------------- /content/home/home.txt: -------------------------------------------------------------------------------- 1 | Title: Home 2 | 3 | ---- 4 | 5 | Text: 6 | 7 | Yay! If you are seeing this, the installation of Kirby worked :) 8 | Now just give the links in the menu a try to see if URL redirection works as well. 9 | 10 | Check out the docs: if you run into any problems or send me an email: 11 | 12 | Have fun with Kirby! -------------------------------------------------------------------------------- /content/site.txt: -------------------------------------------------------------------------------- 1 | Title: Kirby 2 | ---- 3 | Author: Bastian Allgeier 4 | ---- 5 | Description: Kirby is awesome 6 | ---- 7 | Keywords: kirby, cms, kirbycms, php, filesystem 8 | ---- 9 | Copyright: © 2009-(date: Year) (link: http://bastianallgeier.com text: Bastian Allgeier) -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | in kirby.php 36 | - fixed a potential bug in kirbytext 37 | - fixed a potential bug in the troubleshoot modal 38 | - implemented Lukas' $page->hasTemplate() 39 | - improved markdown configuration 40 | - added smartypants 41 | - fixed a strict bug in the uri class when language support is switched on 42 | - fixed a compatibility issue with Apache on Win where : as param separator in URLs is not allowed 43 | - added a new config var to set content files, which should be ignored. .svn .git and .htaccess files are now ignored by default. Suggested by https://github.com/bastianallgeier/kirbycms/pull/30 44 | - added async script loading to the js() helper function. (https://github.com/bastianallgeier/kirbycms/pull/40) 45 | - fixed a bug in site.php, which broke language switching 46 | - added new ecco and dump helper functions 47 | - added new compatibility check with mbstring functions 48 | - new $pages->nth($n) and $files->nth($n) method 49 | - new operators for filterBy methods (!= and *=) 50 | - Now actually using the config option 'tinyurl.folder' (PR: https://github.com/bastianallgeier/kirbycms/pull/44) 51 | - changed async javascript loading default to false 52 | 53 | ## 1.1.0 54 | 55 | ### Update from 1.0.9 to 1.1.0 56 | 57 | To update from 1.0.9 to 1.1.0 replace the `kirby` system folder. Delete all files in site/cache, if you got caching enabled. 58 | 59 | PLEASE REPLACE YOUR OLD HTACCESS FILE WITH THE NEW HTACCESS FILE!!! 60 | 61 | ### Changes 62 | 63 | - added full multi-language support 64 | - multi-language box fixes 65 | - auto-subfolder detection 66 | - removed the favicons from the default template to keep it more simple 67 | - fixed some inconsistencies in the templates 68 | - improved the config 69 | - all invisible content files are now skipped 70 | - added a Kirby Panel Version check. 71 | - fixed a caching bug 72 | - changeable content file extensions 73 | - new improved handling of plugins in the plugins folder (by Lukas) 74 | 75 | ## 1.0.9 76 | 77 | ### Update from 1.0.8 to 1.0.9 78 | 79 | To update from 1.0.8 to 1.0.9 replace the `kirby` system folder. Delete all files in site/cache, if you got caching enabled. 80 | 81 | ### Changes 82 | 83 | - fixed a bug in pages::filterBy(). 84 | - fixed a possible php error in kirbytext::init() 85 | - fixed a bug in pages::without() 86 | - proper 404 header for error pages 87 | - improved uri::raw() method 88 | - added optional php markdown extra parser 89 | - updated to kirby toolkit 0.930 90 | - new files::filterBy() method 91 | 92 | ## 1.0.8 93 | 94 | ### Update from 1.0.7 to 1.0.8 95 | 96 | To update from 1.0.7 to 1.0.8 replace the `kirby` system folder. Delete all files in site/cache, if you got caching enabled. 97 | 98 | ### Changes 99 | 100 | - fixed a bug in pages::findBy(). Strict comparison did break comparing strings. 101 | - added a yaml parser 102 | 103 | ## 1.0.7 104 | 105 | ### Update from 1.0.6 to 1.0.7 106 | 107 | To update from 1.0.6 to 1.0.7 replace the `kirby` system folder. Delete all files in site/cache, if you got caching enabled. 108 | 109 | ### Changes 110 | 111 | - made sure the .htaccess file is no longer a binary to avoid permission problems. 112 | - fixed a bug with invalid variable names in content files. They get converted to valid function names now. 113 | - added strict comparison for objects to improve performance and avoid nested errors. 114 | - added $pages->files()->flip() to be compliant with the pages class 115 | - added a recommended values section to the htaccess file. 116 | 117 | ## 1.0.6 118 | 119 | ### Update from 1.0.5 to 1.0.6 120 | 121 | To update from 1.0.5 to 1.0.6 replace the `kirby` system folder. 122 | 123 | ### Changes 124 | 125 | - fixed a bug with numeric folder names (2010, 1234, etc.) 126 | - added rel="" to the kirbytext link tag: (link: http://google.com rel: nofollow) 127 | 128 | ## 1.0.5 129 | 130 | ### Update from 1.0.4 to 1.0.5 131 | 132 | To update from 1.0.4 to 1.0.5 replace the `kirby` system folder. 133 | 134 | ### Changes 135 | 136 | - it's now possible to have subdirectory-less blog article urls. 137 | - fixed a bug in parsers/kirbytext.php 138 | - fixed a bug in lib/files.php 139 | - updated the toolkit to 0.929 140 | 141 | ## 1.0.4 142 | 143 | ### Update from 1.0.3 to 1.0.4 144 | 145 | To update from 1.0.3 to 1.0.4 replace the `kirby` system folder. 146 | 147 | ### Changes 148 | 149 | - new cache::expired() method and better cache::get() method 150 | - removed the c::set('root.content',…) rule from the config file. It breaks setting the content directory in the index.php 151 | - fixed a bug in str::split and updated to kirby toolkit 0.9.2.7 152 | - fixed a bug in page::parseName() 153 | - fixed a bug in kirbytext which caused a fatal error in older php versions 154 | - more logical instructions on the subfolder modal 155 | - fixed a possible bug in kirby/lib/variables.php 156 | 157 | ## 1.0.3 158 | 159 | ### Update from 1.0.2 to 1.0.3 160 | 161 | To update from 1.0.2 to 1.0.3 replace the `kirby` system folder. 162 | 163 | ### Changes 164 | 165 | - date fields can now be sorted correctly with sortBy 166 | - kirbytext can now be extended 167 | - new param() helper function 168 | - new $pages->filterBy() method 169 | - new way to load library files 170 | 171 | ## 1.0.2 172 | 173 | ### Update from 1.0.1 to 1.0.2 174 | 175 | To update from 1.0.1 to 1.0.2 replace the `kirby` system folder and remove the old changelog.mdown file from your main site folder. The new changelog file is now inside the kirby system folder and will update automatically with each new download. 176 | 177 | ### Changes 178 | 179 | - bug fix in kirby/lib/helpers.php – snippet() 180 | - moved the changelog to the kirby folder so you get an updated version with each update. 181 | - fixed $pages->sortBy() and $page->files->sortBy() 182 | - switched to kirby toolkit 0.925 183 | - added an array of custom variables to the $site object 184 | - added class and title attributes wherever possible in kirbytext tags 185 | - added pagination.variable and pagination.method to kirby/defaults.php to keep them adjustable. 186 | 187 | ## 1.0.1 188 | 189 | ### Update from 1.0 to 1.0.1 190 | 191 | To update from 1.0 to 1.0.1 replace the `kirby` system folder. 192 | 193 | ### Changes 194 | 195 | - switched from flash embeds for Youtube and Vimeo videos in kirbytext to iframe embedded videos, to make them work on mobile devices. Thanks to @nirusu for the suggestion. 196 | - merged the pull request from nebelschwade: https://github.com/bastianallgeier/kirbycms/pull/1, which removes the default type attribute in script and link tags and adds improved css media handling. 197 | - added the kirby toolkit 0.922 198 | - added $page->depth() to get a numeric representation of the subfolder level. 199 | - fixed the variable splitter for text files. 200 | 201 | ## 1.0 202 | 203 | ### Update from 1.0 RC2 to 1.0: 204 | 205 | To update from 1.0 RC2 to 1.0 replace the `kirby` system folder and replace site/config/config.php if you didn't change anything there. 206 | 207 | ### Changes 208 | 209 | - added the homepage setup to the default config file. 210 | - added the license key setup to the default config file. 211 | 212 | ## 1.0 RC2 213 | 214 | ### Update from 1.0 RC to 1.0 RC2: 215 | 216 | To update from 1.0 RC to 1.0 RC2 replace the `kirby` system folder. 217 | 218 | ### Changes 219 | 220 | - added the latest Kirby Toolkit version (0.92) 221 | - fixed the missing subfolder installation screen 222 | - improved caching again 223 | - added c::set('cache.autoupdate', true); which can be switched off to avoid filesystem access to check for the last change in the content folder. 224 | - fixed a potential bug in $site->modified() 225 | 226 | ## 1.0 RC 227 | 228 | ### Update from 0.9.9.1 to 1.0 RC: 229 | 230 | To update from 0.9.9.1 to 1.0 RC replace the `kirby` system folder. 231 | 232 | ### Changes 233 | 234 | - fixed a bug in `$pages->active()` which broke setting your own home uri 235 | - fixed a bug in `$pages->isOpen()` which didn't mark the homepage as open even if it was 236 | - changed `$pages->get()` and `$files->get()` to `$pages->slice` and `$files->slice`. This should have been already fixed :( 237 | - fixed a bug in almost every count method in the pages class. 238 | - fixed a potential bug in the uri class, for subfolder installations 239 | - changed the caching in `kirby/lib/site.php` to be more effective 240 | - changed the `$page->url()` method to return the homepage url without any path attached. 241 | - fixed a bug in the tinyurl redirection. 242 | 243 | ## 0.9.9.1 244 | 245 | ### Damn, I need more versions :) 246 | 247 | ### Update from 0.9.9.0 to 0.9.9.1: 248 | 249 | To update from 0.9.9 to 0.9.9.1 replace the `kirby` system folder and if you didn't change anything there yet, also the .htaccess file and the site/config/config.php. But that's only cosmetics. 250 | 251 | ### Changes 252 | 253 | - changed the layout of the site/config/config.php to be more easy to understand 254 | - added a default timezone to kirby/config.php 255 | - changed kirby/config.php to kirby/defaults.php to make it clearer that this should not be edited 256 | - added more descriptions and help to the default .htaccess file 257 | - removed kirby/parsers/html.php, kirby/parsers/multiline.php and kirby/parsers/xml.php and merged them all in one kirby/parsers/defaults.php file. 258 | - replaced the assets/images/apple-touch-icon.png and assets/images/favicon.png with newer versions. 259 | - slightly cleaned up the assets/styles/styles.css 260 | - slightly changed the readme.mdown 261 | - slightly changed the license.mdown 262 | - fixed a bug in the pagination class 263 | - the pagination class has two more functions `numStart()` and `numEnd()` 264 | - added c::set('cache.ignore', array()); to make it easy to ignore certain pages while caching. 265 | - made it possible to switch to query pagination instead of params 266 | - added ranged pagination 267 | 268 | ## 0.9.9 269 | 270 | ### Update from 0.9.8 to 0.9.9: 271 | 272 | To update from 0.9.8 to 0.9.9 replace the `kirby` system folder and the index.php 273 | 274 | ### Changes 275 | 276 | - removed `$site->find()` to avoid duplicates and strict errors 277 | - cleaned (hopefully) all `E_STRICT` warnings. 278 | - added a troubleshooting screen which you can activate in your site config, when something is wrong: `c::set('troubleshoot', true)` 279 | - added an improved subfolder setup screen 280 | - added a check for the kirby system file to the index.php 281 | 282 | ## 0.9.8 283 | 284 | ### Update from 0.9.7 to 0.9.8: 285 | 286 | To update from 0.9.7 to 0.9.8 simply replace the `kirby` system folder. 287 | 288 | ### Changes 289 | 290 | - bug fix in pagination class 291 | - new children function for a set of pages, which gathers all children for all pages in the set 292 | - `$pages->find('page-a', 'page-b')` can take multiple page uids now and returns a set of all matches 293 | - the same applies to `$pages->findByUID()`, `$pages->findByDirname()`, `$pages->findByTitle()` and `$pages->findByHash()` 294 | - renamed `$files->get()` and `$pages->get()` into `$files->slice()` and `$pages->slice()` 295 | - cleaned a few things in the uri class 296 | 297 | 298 | ## 0.9.7 299 | 300 | ### Update from 0.9.6 to 0.9.7: 301 | 302 | To update from 0.9.6 to 0.9.7 simply replace the `kirby` system folder. 303 | 304 | ### Changes 305 | 306 | - fixed a bug in kirby/lib/files.php 307 | - new shuffle function for pages and files i.e. `$page->children()->shuffle()` 308 | - switched to kirby toolkit 0.9.1 309 | - another fix for the field detection in text files 310 | 311 | ## 0.9.6 312 | 313 | ### Update from 0.9.5 to 0.9.6: 314 | 315 | To update from 0.9.4 to 0.9.5 simply replace the `kirby` system folder and the .htaccess file. 316 | 317 | ### Changes 318 | 319 | - changed the version number for the kirby toolkit from 0.6 to 0.9 320 | - used a new regular expression to split the fields in text files. 321 | - fixed a bug in the pagination class 322 | - you can now add class names to kirbytext links 323 | - fixed some bugs in kirbytext 324 | - fixed a bug in the url function, which broke sites without url rewriting 325 | 326 | 327 | ## 0.9.5 328 | 329 | ### Update from 0.9.4 to 0.9.5: 330 | 331 | To update from 0.9.4 to 0.9.5 simply replace the `kirby` system folder and the .htaccess file. 332 | 333 | ### Changes 334 | 335 | - added a `highlight()` hook for the `kirbytext()` parser to enable custom code highlighting 336 | - added ``` ``` code blocks to kirbytext 337 | - fixed a notice bug in kirbytext 338 | - added config var for markdown breaks 339 | - small change in the `.htaccess` file to make `/file:image.jpg` urls possible 340 | - added local file urls i.e. `(http://yourdomain.com/blog/file:01.png)` 341 | - fixed a small bug in `a::sort()` function 342 | - fixed bug in files fetcher 343 | - removed the txt class and added variable fetching to the variables class 344 | - better video support in `kirby/lib/files.php` 345 | - fixed a bug in the `excerpt()` function 346 | - Revamped the URI class. 347 | 348 | 349 | ## 0.9.4 350 | 351 | ### Update from 0.9.3 to 0.9.4: 352 | 353 | To update from 0.9.3 to 0.9.4 simply replace the `kirby` system folder. Also replace the .htaccess file if you got problems with the php_flag short_open_tag on directive. 354 | 355 | ### Changes 356 | 357 | - disabled `php_flag short_open_tag on` in the .htaccess file by default as it causes problems on some servers. 358 | - gist embedding in kirbytext 359 | - `excerpt($text, 0)` returns the entire text but without html 360 | - automatic detection of misconfigured subfolder installation 361 | - fixed a bug in kirbytext 362 | - added new variable object for all custom variables of a page 363 | - all query objects fail silently now when converted to a string. They will actually return some nice informative html. So now you can do stuff like `` or `find('blog') ?>` without error messages. 364 | - new `$page->modified($format=false)` function 365 | - new `$site->url()` function 366 | - new `$site->find($uri)` function, which is a alternative for `$pages->find($uri)` 367 | 368 | ## 0.9.3 369 | 370 | ### Update from 0.9.2 to 0.9.3: 371 | 372 | To update from 0.9.1 to 0.9.2 simply replace the `kirby` system folder. 373 | 374 | ### Changes 375 | 376 | - added a more verbose readme.mdown with more docs about the installation process. 377 | - added a new xml parser, which generates xml safe output for strings. This can be used for feeds etc. 378 | - totally rebuilt the file reading engine, which is way faster now – especially with deep file structures 379 | - added a new pagination class, which makes it super easy to add pagination to sets of pages and sets of files: http://getkirby.com/docs/variables/pages 380 | - added a new `$pages->not($uid)` function which is a shortcut for `$pages->without($uid)` and is more jquerish 381 | - also added a new `$files->not($name)` 382 | - added `$pages->limit($limit)` and `$files->limit($limit)` 383 | - added `$pages->offset($offset)` and `$files->offset($offset)` 384 | - added a better a::sort function to the kirby toolkit 385 | - added a dir::inspect function to the kirby toolkit and removed the fdb class instead. 386 | - replaced the dir::read function code with faster/easier scandir code. 387 | - added a `$page->date($format)` function which makes it easy to specify a date variable for a page and afterwards get and format it in templates. 388 | - fixed a bug in the excerpt function 389 | - added $pages->findOpen() 390 | - added $page->isOpen() 391 | - changed the default template. 392 | 393 | 394 | ## 0.9.2 395 | 396 | ### Update from 0.9.1 to 0.9.2: 397 | 398 | To update from 0.9.1 to 0.9.2 simply replace the `kirby` system folder and the `index.php`. 399 | If you want to start from scratch, just replace the entire code. 400 | 401 | ### Changes 402 | 403 | - new `$pages->flip()` function, which returns a set of pages in reverse order 404 | - new `str::excerpt($string, $chars=140, $removehtml=true, $rep='…')` function in kirby/lib/kirby.php 405 | - new `excerpt($string, $chars=140)` function in kirby/parsers/kirbytext.php 406 | - removed default error display in kirby/system.php 407 | - removed `ini_set` for php short open tags in kirby/system.php 408 | - added a `date_default_timezone_set` to kirby/system.php 409 | - added a custom config var + description for custom timezones to site/config.php 410 | - drastically reduced the templates and snippets code 411 | - moved images, javascript and styles to an asset folder. 412 | - slightly adapted the style of the demo template to the kirby site. 413 | - removed the plugins folder when not needed. 414 | - removed all php short open tags from the default template to increase compatibility 415 | - redesigned the index.php and kirby/system.php to keep path definitions more flexible. 416 | 417 | ## 0.9.1 418 | 419 | ### Changes 420 | 421 | - cleaned up templates 422 | - trying to enable short open tags in .htaccess file. -------------------------------------------------------------------------------- /kirby/defaults.php: -------------------------------------------------------------------------------- 1 | url() . '">' . $this->url() . ''; 12 | } 13 | 14 | function meta($code=false) { 15 | if(!$code) $code = c::get('lang.default'); 16 | return new obj(a::get($this->meta, $code)); 17 | } 18 | 19 | function next() { 20 | 21 | if($this->next) return $this->next; 22 | 23 | $parent = $this->parent(); 24 | if(!$parent) return false; 25 | $siblings = $parent->findByType($this->type); 26 | $index = $siblings->indexOf($this); 27 | if($index === false) return false; 28 | 29 | $siblings = array_values($siblings->toArray()); 30 | $nextIndex = $index+1; 31 | return a::get($siblings, $nextIndex); 32 | } 33 | 34 | function hasNext() { 35 | return ($this->next()) ? true : false; 36 | } 37 | 38 | function prev() { 39 | 40 | if($this->prev) return $this->prev; 41 | 42 | $parent = $this->parent(); 43 | if(!$parent) return false; 44 | $siblings = $parent->findByType($this->type); 45 | $index = $siblings->indexOf($this); 46 | if($index === false) return false; 47 | 48 | $siblings = array_values($siblings->toArray()); 49 | $prevIndex = $index-1; 50 | return a::get($siblings, $prevIndex); 51 | } 52 | 53 | function hasPrev() { 54 | return ($this->prev()) ? true : false; 55 | } 56 | 57 | function url() { 58 | return c::get('url') . '/' . $this->uri; 59 | } 60 | 61 | function info() { 62 | 63 | if($this->info) return $this->info; 64 | 65 | $info = array( 66 | 'size' => f::size($this->root), 67 | 'mime' => (function_exists('mime_content_type')) ? @mime_content_type($this->root) : false 68 | ); 69 | 70 | // set the nice size 71 | $info['niceSize'] = f::nice_size($info['size']); 72 | 73 | return $this->info = new obj($info); 74 | 75 | } 76 | 77 | function size() { 78 | $info = $this->info(); 79 | return $info->size(); 80 | } 81 | 82 | function niceSize() { 83 | $info = $this->info(); 84 | return $info->niceSize(); 85 | } 86 | 87 | function mime() { 88 | $info = $this->info(); 89 | return $info->mime(); 90 | } 91 | 92 | } 93 | 94 | class image extends file { 95 | 96 | function __construct($array=array()) { 97 | parent::__construct($array); 98 | $this->thumb = $this; 99 | $this->title = $this->name; 100 | } 101 | 102 | function width() { 103 | $info = $this->info(); 104 | return $info->width(); 105 | } 106 | 107 | function height() { 108 | $info = $this->info(); 109 | return $info->height(); 110 | } 111 | 112 | function fit($box, $force=false) { 113 | $size = size::fit($this->width(), $this->height(), $box, $force); 114 | $this->info->width = $size['width']; 115 | $this->info->height = $size['height']; 116 | return $this; 117 | } 118 | 119 | function fitWidth($width, $force=false) { 120 | $size = size::fit_width($this->width(), $this->height(), $width, $force); 121 | $this->info->width = $size['width']; 122 | $this->info->height = $size['height']; 123 | return $this; 124 | } 125 | 126 | function fitHeight($height, $force=false) { 127 | $size = size::fit_height($this->width(), $this->height(), $height, $force); 128 | $this->info->width = $size['width']; 129 | $this->info->height = $size['height']; 130 | return $this; 131 | } 132 | 133 | function info() { 134 | 135 | if($this->info) return $this->info; 136 | 137 | $info = parent::info(); 138 | $size = @getimagesize($this->root); 139 | 140 | if(!$size) { 141 | $info->width = false; 142 | $info->height = false; 143 | } else { 144 | $info->width = $size[0]; 145 | $info->height = $size[1]; 146 | $info->mime = $size['mime']; 147 | } 148 | 149 | return $this->info = $info; 150 | 151 | } 152 | 153 | } 154 | 155 | class video extends file { 156 | 157 | function __construct($array=array()) { 158 | parent::__construct($array); 159 | } 160 | 161 | function mime() { 162 | 163 | switch($this->extension) { 164 | case 'ogg': 165 | case 'ogv': 166 | return 'video/ogg'; 167 | case 'webm': 168 | return 'video/webm'; 169 | case 'mp4': 170 | return 'video/mp4'; 171 | } 172 | 173 | $info = $this->info(); 174 | return $info->mime(); 175 | 176 | } 177 | 178 | } 179 | 180 | 181 | class files extends obj { 182 | 183 | var $pagination = null; 184 | 185 | function __toString() { 186 | $output = array(); 187 | foreach($this->_ as $key => $file) { 188 | $output[] = $file . '
'; 189 | } 190 | return implode("\n", $output); 191 | } 192 | 193 | function init($page) { 194 | 195 | foreach($page->rawfiles AS $key => $file) { 196 | 197 | // skip invisible files 198 | if(preg_match('!^\.!', $file)) continue; 199 | 200 | $info = array( 201 | 'name' => f::name($file), 202 | 'filename' => $file, 203 | 'extension' => f::extension($file), 204 | 'root' => $page->root . '/' . $file, 205 | 'uri' => $page->diruri . '/' . $file, 206 | 'parent' => $this, 207 | 'modified' => @filectime($page->root . '/' . $file) 208 | ); 209 | 210 | switch($info['extension']) { 211 | case 'jpg': 212 | case 'jpeg': 213 | case 'gif': 214 | case 'png': 215 | $info['type'] = 'image'; 216 | $class = 'image'; 217 | break; 218 | case 'pdf': 219 | case 'doc': 220 | case 'xls': 221 | case 'ppt': 222 | $info['type'] = 'document'; 223 | $class = 'file'; 224 | break; 225 | case 'mov': 226 | case 'avi': 227 | case 'ogg': 228 | case 'ogv': 229 | case 'webm': 230 | case 'flv': 231 | case 'swf': 232 | case 'mp4': 233 | $info['type'] = 'video'; 234 | $class = 'video'; 235 | break; 236 | case 'm4a': 237 | case 'mp3': 238 | $info['type'] = 'sound'; 239 | $class = 'file'; 240 | break; 241 | case c::get('content.file.extension', 'txt'): 242 | $info['type'] = 'content'; 243 | $class = 'variables'; 244 | break; 245 | default: 246 | $info['type'] = 'other'; 247 | $class = 'file'; 248 | } 249 | 250 | $this->$file = new $class($info); 251 | 252 | } 253 | 254 | $this->dispatchImages(); 255 | $this->dispatchContent(); 256 | 257 | } 258 | 259 | function dispatchImages() { 260 | 261 | foreach($this->images() as $key => $image) { 262 | 263 | // check for images with thumbnail naming 264 | if(preg_match('!\.thumb!', $image->name)) { 265 | 266 | // get the rawFilename of the original file to which 267 | // this thumb belongs to 268 | $rawFilename = str_replace('.thumb', '', $image->filename); 269 | 270 | // find the original size 271 | $original = $this->find($rawFilename); 272 | 273 | // if there's no original skip this 274 | if(!$original) continue; 275 | 276 | // attach the thumbnail to the original 277 | $original->thumb = $image; 278 | 279 | // remove it from the list of files 280 | unset($this->_[$key]); 281 | 282 | } 283 | 284 | } 285 | 286 | } 287 | 288 | function dispatchContent() { 289 | 290 | $meta = array(); 291 | 292 | $langSupport = c::get('lang.support'); 293 | $translated = c::get('lang.translated'); 294 | 295 | foreach($this->contents() as $key => $content) { 296 | 297 | // split filenames (already without extension) by . 298 | $parts = explode('.', $content->name); 299 | $countParts = count($parts); 300 | $lastPart = a::last($parts); 301 | $firstPart = a::first($parts); 302 | 303 | // home.txt 304 | if($countParts == 1) { 305 | 306 | // files without a language code 307 | // are considered to be the default language file 308 | $content->languageCode = c::get('lang.default'); 309 | 310 | // keep the entire name for the template (i.e. home) 311 | $content->template = $content->name; 312 | 313 | // home.en.txt 314 | // myfile.jpg.txt 315 | // article.video.txt 316 | } else if($countParts == 2) { 317 | 318 | // check for a matching file by the entire name 319 | $file = $this->find($content->name); 320 | 321 | // myfile.jpg.txt 322 | if($file) { 323 | 324 | // change the filetype 325 | $content->type = 'meta'; 326 | 327 | // files without a language code 328 | // are considered to be the default language file 329 | $content->languageCode = c::get('lang.default'); 330 | $file->meta[$content->languageCode] = $content->variables; 331 | 332 | // add this to the meta array 333 | $meta[] = $file; 334 | 335 | // home.en.txt 336 | // article.video.txt 337 | } else { 338 | 339 | // check for a valid language extension 340 | // home.en.txt 341 | if($langSupport && in_array($lastPart, c::get('lang.available', array()))) { 342 | 343 | // use the first part for the template name (i.e. home) 344 | $content->template = $firstPart; 345 | 346 | // add the language code 347 | $content->languageCode = $lastPart; 348 | 349 | // plain content file with crazy name 350 | // article.video.txt 351 | } else { 352 | 353 | // files without a language code 354 | // are considered to be the default language file 355 | $content->languageCode = c::get('lang.default'); 356 | 357 | // use the entire name for the template (i.e. home) 358 | $content->template = $content->name; 359 | 360 | } 361 | 362 | } 363 | 364 | 365 | // myfile.jpg.de.txt 366 | // article.video.de.txt 367 | // something more absurd 368 | } else if($countParts > 2) { 369 | 370 | // check for a valid language extension 371 | // myfile.jpg.de.txt 372 | // article.video.de.txt 373 | if($langSupport && in_array($lastPart, c::get('lang.available', array()))) { 374 | 375 | // name without the last part / language code 376 | $name = implode('.', array_slice($parts, 0, -1)); 377 | 378 | // check for a matching file by the new name 379 | $file = $this->find($name); 380 | 381 | // add the language code 382 | $content->languageCode = $lastPart; 383 | 384 | // myfile.jpg.de.txt 385 | if($file) { 386 | 387 | // change the filetype 388 | $content->type = 'meta'; 389 | $file->meta[$content->languageCode] = $content->variables; 390 | 391 | // add this to the meta array 392 | $meta[] = $file; 393 | 394 | // article.video.de.txt 395 | } else { 396 | 397 | // use the already prepared name for the template (i.e. article.video) 398 | $content->template = $name; 399 | 400 | } 401 | 402 | // something more absurd 403 | // article.video.whatever.txt 404 | // myfile.something.jpg.txt 405 | // or an invalid language code 406 | } else { 407 | 408 | // check for a matching file by the new name 409 | $file = $this->find($content->name); 410 | 411 | // files without a language code 412 | // are considered to be the default language file 413 | $content->languageCode = c::get('lang.default'); 414 | 415 | if($file) { 416 | 417 | $content->type = 'meta'; 418 | $file->meta[$content->languageCode] = $content->variables; 419 | 420 | // add this to the meta array 421 | $meta[] = $file; 422 | 423 | } else { 424 | 425 | // use the entire name for the template (i.e. article.video.whatever) 426 | $content->template = $content->name; 427 | 428 | } 429 | 430 | } 431 | 432 | } 433 | 434 | } 435 | 436 | foreach($meta as $m) { 437 | 438 | if($langSupport) { 439 | 440 | $variables = (array)a::get($m->meta, c::get('lang.default')); 441 | 442 | if($translated) { 443 | $translation = (array)a::get($m->meta, c::get('lang.current')); 444 | $variables = (!empty($translation)) ? array_merge($variables, $translation) : $variables; 445 | } 446 | 447 | } else { 448 | $variables = (array)@a::first($m->meta); 449 | } 450 | 451 | // merge the variables with the file object 452 | $m->_ = array_merge($m->_, $variables); 453 | 454 | } 455 | 456 | } 457 | 458 | function content() { 459 | return $this->content; 460 | } 461 | 462 | function slice($offset=null, $limit=null) { 463 | if($offset === null && $limit === null) return $this; 464 | return new files(array_slice($this->_, $offset, $limit)); 465 | } 466 | 467 | function limit($limit) { 468 | return $this->slice(0, $limit); 469 | } 470 | 471 | function offset($offset) { 472 | return $this->slice($offset); 473 | } 474 | 475 | function without($name) { 476 | $files = $this->_; 477 | unset($files[$name]); 478 | return new files($files); 479 | } 480 | 481 | function not($name) { 482 | return $this->without($name); 483 | } 484 | 485 | function find() { 486 | 487 | $args = func_get_args(); 488 | 489 | // find multiple files 490 | if(count($args) > 1) { 491 | $result = array(); 492 | foreach($args as $arg) { 493 | $file = $this->find($arg); 494 | if($file) $result[$file->filename] = $file; 495 | } 496 | return (empty($result)) ? false : new files($result); 497 | } 498 | 499 | // find a single file 500 | $key = a::first($args); 501 | if(!$key) return $this->_; 502 | return a::get($this->_, $key); 503 | } 504 | 505 | function findByExtension() { 506 | 507 | $args = func_get_args(); 508 | $count = count($args); 509 | if($count == 0) return false; 510 | 511 | $files = array(); 512 | foreach($this->_ as $key => $file) { 513 | if($count > 1) { 514 | if(in_array($file->extension, $args)) $files[$key] = $file; 515 | } else { 516 | if($file->extension == $args[0]) $files[$key] = $file; 517 | } 518 | } 519 | return new files($files); 520 | } 521 | 522 | function findByType($type) { 523 | 524 | $args = func_get_args(); 525 | $count = count($args); 526 | if($count == 0) return false; 527 | 528 | $files = array(); 529 | foreach($this->_ as $key => $file) { 530 | if($count > 1) { 531 | if(in_array($file->type, $args)) $files[$key] = $file; 532 | } else { 533 | if($file->type == $args[0]) $files[$key] = $file; 534 | } 535 | } 536 | return new files($files); 537 | } 538 | 539 | function filterBy() { 540 | 541 | $args = func_get_args(); 542 | $field = a::get($args, 0); 543 | $operator = '=='; 544 | $value = a::get($args, 1); 545 | $split = a::get($args, 2); 546 | 547 | if($value === '!=' || $value === '==' || $value === '*=') { 548 | $operator = $value; 549 | $value = a::get($args, 2); 550 | $split = a::get($args, 3); 551 | } 552 | 553 | $files = array(); 554 | 555 | switch($operator) { 556 | 557 | // ignore matching elements 558 | case '!=': 559 | 560 | foreach($this->_ as $key => $file) { 561 | if($split) { 562 | $values = str::split((string)$file->$field(), $split); 563 | if(!in_array($value, $values)) $files[$key] = $file; 564 | } else if($file->$field() != $value) { 565 | $files[$key] = $file; 566 | } 567 | } 568 | break; 569 | 570 | // search 571 | case '*=': 572 | 573 | foreach($this->_ as $key => $file) { 574 | if($split) { 575 | $values = str::split((string)$file->$field(), $split); 576 | foreach($values as $val) { 577 | if(str::contains($val, $value)) { 578 | $files[$key] = $file; 579 | break; 580 | } 581 | } 582 | } else if(str::contains($file->$field(), $value)) { 583 | $files[$key] = $file; 584 | } 585 | } 586 | 587 | // take all matching elements 588 | default: 589 | 590 | foreach($this->_ as $key => $file) { 591 | if($split) { 592 | $values = str::split((string)$file->$field(), $split); 593 | if(in_array($value, $values)) $files[$key] = $file; 594 | } else if($file->$field() == $value) { 595 | $files[$key] = $file; 596 | } 597 | } 598 | 599 | break; 600 | 601 | } 602 | 603 | return new files($files); 604 | 605 | } 606 | 607 | function images() { 608 | return $this->findByType('image'); 609 | } 610 | 611 | function videos() { 612 | return $this->findByType('video'); 613 | } 614 | 615 | function documents() { 616 | return $this->findByType('document'); 617 | } 618 | 619 | function sounds() { 620 | return $this->findByType('sound'); 621 | } 622 | 623 | function contents() { 624 | return $this->findByType('content'); 625 | } 626 | 627 | function meta() { 628 | return $this->findByType('meta'); 629 | } 630 | 631 | function others() { 632 | return $this->findByType('other'); 633 | } 634 | 635 | function totalSize() { 636 | $size = 0; 637 | foreach($this->_ as $file) { 638 | $size = $size + $file->size(); 639 | } 640 | return $size; 641 | } 642 | 643 | function niceTotalSize() { 644 | return f::nice_size($this->totalSize()); 645 | } 646 | 647 | function flip() { 648 | $files = array_reverse($this->_, true); 649 | return new files($files); 650 | } 651 | 652 | function sortBy($field, $direction='asc', $method=SORT_REGULAR) { 653 | $files = a::sort($this->_, $field, $direction, $method); 654 | return new files($files); 655 | } 656 | 657 | function paginate($limit, $options=array()) { 658 | 659 | $pagination = new pagination($this, $limit, $options); 660 | $files= $this->slice($pagination->offset, $pagination->limit); 661 | $files->pagination = $pagination; 662 | 663 | return $files; 664 | 665 | } 666 | 667 | function pagination() { 668 | return $this->pagination; 669 | } 670 | 671 | } 672 | 673 | -------------------------------------------------------------------------------- /kirby/lib/helpers.php: -------------------------------------------------------------------------------- 1 | uri->toURL(); 49 | } 50 | 51 | // go home 52 | function home() { 53 | go(url()); 54 | } 55 | 56 | // go to error page 57 | function notFound() { 58 | go(url('error')); 59 | } 60 | 61 | // embed a template snippet from the snippet folder 62 | function snippet($snippet, $data=array(), $return=false) { 63 | return tpl::loadFile(c::get('root.snippets') . '/' . $snippet . '.php', $data, $return); 64 | } 65 | 66 | // embed a stylesheet tag 67 | function css($url, $media=false) { 68 | $url = (str::match($url, '~(^\/\/|^https?:\/\/)~'))? $url : url(ltrim($url, '/')); 69 | if(!empty($media)) { 70 | return '' . "\n"; 71 | } else { 72 | return '' . "\n"; 73 | } 74 | } 75 | 76 | // embed a js tag 77 | function js($url, $async = false) { 78 | $url = (str::match($url, '~(^\/\/|^https?:\/\/)~'))? $url : url(ltrim($url, '/')); 79 | $async = ($async) ? ' async' : ''; 80 | return '' . "\n"; 81 | } 82 | 83 | // fetch a param from the URI 84 | function param($key, $default=false) { 85 | global $site; 86 | return $site->uri->params($key, $default); 87 | } 88 | 89 | // smart version of echo with an if condition as first argument 90 | function ecco($condition, $echo, $alternative = false) { 91 | echo ($condition) ? $echo : $alternative; 92 | } 93 | 94 | function dump($var) { 95 | return a::show($var); 96 | } -------------------------------------------------------------------------------- /kirby/lib/load.php: -------------------------------------------------------------------------------- 1 | _ = $array; 23 | } 24 | 25 | function __set($n, $v) { 26 | $this->_[$n] = $v; 27 | } 28 | 29 | function __get($n) { 30 | return a::get($this->_, $n); 31 | } 32 | 33 | function __call($n, $args) { 34 | return a::get($this->_, strtolower($n)); 35 | } 36 | 37 | function rewind() { 38 | reset($this->_); 39 | } 40 | 41 | function current() { 42 | return current($this->_); 43 | } 44 | 45 | function key() { 46 | return key($this->_); 47 | } 48 | 49 | function next() { 50 | return next($this->_); 51 | } 52 | 53 | function prev() { 54 | return prev($this->_); 55 | } 56 | 57 | function nth($n) { 58 | $array = array_values($this->_); 59 | return (isset($array[$n])) ? $array[$n] : false; 60 | } 61 | 62 | function valid() { 63 | $key = key($this->_); 64 | $var = ($key !== NULL && $key !== FALSE); 65 | return $var; 66 | } 67 | 68 | function find() { 69 | 70 | $args = func_get_args(); 71 | $key = @$args[0]; 72 | $default = @$args[1]; 73 | 74 | if(!$key) return $this->_; 75 | return a::get($this->_, $key, $default); 76 | } 77 | 78 | function count() { 79 | return count($this->_); 80 | } 81 | 82 | function first() { 83 | return a::first($this->_); 84 | } 85 | 86 | function last() { 87 | return a::last($this->_); 88 | } 89 | 90 | function indexOf($needle) { 91 | foreach(array_values($this->_) as $key => $value) { 92 | if($value === $needle) return $key; 93 | } 94 | return false; 95 | } 96 | 97 | function shuffle() { 98 | $this->_ = a::shuffle($this->_); 99 | return $this; 100 | } 101 | 102 | function toArray() { 103 | return $this->_; 104 | } 105 | 106 | } 107 | 108 | -------------------------------------------------------------------------------- /kirby/lib/pages.php: -------------------------------------------------------------------------------- 1 | children = array(); 10 | $this->online = true; 11 | $this->visible = true; 12 | } 13 | 14 | function __toString() { 15 | return '' . $this->url() . ''; 16 | } 17 | 18 | function content($code=false) { 19 | 20 | // return the cached version if available 21 | if(!$code && is_a($this->content, 'obj')) return $this->content; 22 | 23 | // make sure there's the right code for lang support 24 | if(!$code && c::get('lang.support')) $code = c::get('lang.current'); 25 | 26 | $content = ($code) ? $this->contents()->filterBy('languageCode', $code)->first() : $this->contents()->first(); 27 | $result = array(); 28 | 29 | if($content) { 30 | 31 | foreach($content->variables as $key => $var) { 32 | $result[$key] = new variable($var, $this); 33 | } 34 | 35 | // pass on the variables object and the raw filecontent 36 | $result['variables'] = $content->variables; 37 | $result['filecontent'] = $content->filecontent; 38 | 39 | return new pagecontent($result); 40 | 41 | } 42 | 43 | return false; 44 | 45 | } 46 | 47 | function children($offset=null, $limit=null, $sort='dirname', $direction='asc') { 48 | 49 | // if children have already been fetched return them from "cache" 50 | if(is_object($this->children)) return $this->children->sortBy($sort, $direction); 51 | 52 | $pages = array(); 53 | $ignore = array_merge(array('.svn', '.git', '.hg', '.htaccess'), (array)c::get('content.file.ignore', array())); 54 | 55 | foreach($this->children as $child) { 56 | 57 | $child = dir::inspect($this->root . '/' . $child, $ignore); 58 | $page = page::fromDir($child, $this); 59 | 60 | $pages[$page->uid] = $page; 61 | 62 | } 63 | 64 | $this->children = $children = new pages($pages); 65 | 66 | if($offset || $limit) $children = $children->slice($offset, $limit); 67 | $children = $children->sortBy($sort, $direction); 68 | 69 | return $children; 70 | 71 | } 72 | 73 | function hasChildren() { 74 | return ($this->countChildren() > 0) ? true : false; 75 | } 76 | 77 | function countChildren() { 78 | return $this->children()->count(); 79 | } 80 | 81 | function siblings() { 82 | global $site; 83 | $parent = $this->parent(); 84 | return (!$parent) ? $site->pages : $parent->children(); 85 | } 86 | 87 | function _next($siblings, $sort=false, $direction='asc') { 88 | if($sort) $siblings = $siblings->sortBy($sort, $direction); 89 | $index = $siblings->indexOf($this); 90 | if($index === false) return false; 91 | $siblings = array_values($siblings->toArray()); 92 | $nextIndex = $index+1; 93 | return $this->next = a::get($siblings, $nextIndex); 94 | } 95 | 96 | function _prev($siblings, $sort=false, $direction='asc') { 97 | if($sort) $siblings = $siblings->sortBy($sort, $direction); 98 | $index = $siblings->indexOf($this); 99 | if($index === false) return false; 100 | $siblings = array_values($siblings->toArray()); 101 | $prevIndex = $index-1; 102 | return $this->prev = a::get($siblings, $prevIndex); 103 | } 104 | 105 | function next($sort=false, $direction='asc') { 106 | return $this->_next($this->siblings(), $sort, $direction); 107 | } 108 | 109 | function nextVisible($sort=false, $direction='asc') { 110 | return $this->_next($this->siblings()->visible(), $sort, $direction); 111 | } 112 | 113 | function hasNext($sort=false, $direction='asc') { 114 | return ($this->next($sort, $direction)) ? true : false; 115 | } 116 | 117 | function hasNextVisible($sort=false, $direction='asc') { 118 | return ($this->nextVisible($sort, $direction)) ? true : false; 119 | } 120 | 121 | function prev($sort=false, $direction='asc') { 122 | return $this->_prev($this->siblings(), $sort, $direction); 123 | } 124 | 125 | function prevVisible($sort=false, $direction='asc') { 126 | return $this->_prev($this->siblings()->visible(), $sort, $direction); 127 | } 128 | 129 | function hasPrev($sort=false, $direction='asc') { 130 | return ($this->prev($sort, $direction)) ? true : false; 131 | } 132 | 133 | function hasPrevVisible($sort=false, $direction='asc') { 134 | return ($this->prevVisible($sort, $direction)) ? true : false; 135 | } 136 | 137 | function template() { 138 | 139 | $name = (!$this->intendedTemplate) ? c::get('tpl.default') : $this->intendedTemplate; 140 | 141 | // construct the template file 142 | $file = c::get('root.templates') . '/' . $name . '.php'; 143 | 144 | // check if the template file exists and go back to the fallback 145 | if(!file_exists($file)) $name = c::get('tpl.default'); 146 | 147 | return $name; 148 | 149 | } 150 | 151 | function hasTemplate() { 152 | return ($this->template() == $this->intendedTemplate()) ? true : false; 153 | } 154 | 155 | function depth() { 156 | $parent = $this->parent(); 157 | return ($parent) ? ($parent->depth() + 1) : 1; 158 | } 159 | 160 | function hash() { 161 | if($this->hash) return $this->hash; 162 | 163 | // add a unique hash 164 | $checksum = sprintf('%u', crc32($this->uri)); 165 | return $this->hash = base_convert($checksum, 10, 36); 166 | } 167 | 168 | function url($lang=false) { 169 | if($this->isHomePage() && !c::get('home.keepurl')) { 170 | return url(false, $lang); 171 | } else if(c::get('lang.support') && $lang) { 172 | 173 | $obj = $this; 174 | $cnt = $this->content($lang); 175 | $uri = $cnt ? $cnt->url_key() : false; 176 | if(!$uri) $uri = $this->uid; 177 | 178 | while($parent = $obj->parent()) { 179 | 180 | $cnt = $parent->content($lang); 181 | $uid = ($cnt) ? $cnt->url_key() : false; 182 | if(!$uid) $uid = $parent->uid; 183 | 184 | $uri = $uid . '/' . $uri; 185 | $obj = $obj->parent(); 186 | } 187 | 188 | $uri = $uri; 189 | return u($uri, $lang); 190 | 191 | } else { 192 | return u(($this->translatedURI != '') ? $this->translatedURI : $this->uri); 193 | } 194 | } 195 | 196 | function tinyurl() { 197 | return u(c::get('tinyurl.folder') . '/' . $this->hash()); 198 | } 199 | 200 | function date($format=false) { 201 | if(!$this->date) return false; 202 | $date = strtotime($this->_['date']); 203 | return ($format) ? date($format, $date) : $date; 204 | } 205 | 206 | function modified($format=false) { 207 | return ($format) ? date($format, $this->modified) : $this->modified; 208 | } 209 | 210 | function isHomePage() { 211 | return ($this->uri === c::get('home')) ? true : false; 212 | } 213 | 214 | function isErrorPage() { 215 | return ($this->uri === c::get('404')) ? true : false; 216 | } 217 | 218 | function isActive() { 219 | global $site; 220 | return ($site->pages->active() === $this); 221 | } 222 | 223 | function isOpen() { 224 | 225 | if($this->isOpen) return $this->isOpen; 226 | 227 | global $site; 228 | 229 | if($this->isActive()) return true; 230 | 231 | $p = (c::get('lang.support')) ? str::split($this->translatedURI(), '/'): str::split($this->uri(), '/'); 232 | $u = $site->uri->path->toArray(); 233 | 234 | for($x=0; $xisOpen = false; 236 | } 237 | 238 | return $this->isOpen = true; 239 | 240 | } 241 | 242 | function isVisible() { 243 | return $this->visible; 244 | } 245 | 246 | function isOnline() { 247 | return $this->online; 248 | } 249 | 250 | function isChildOf($obj) { 251 | if($this === $obj); 252 | return ($this->parent() === $obj); 253 | } 254 | 255 | function isAncestorOf($obj) { 256 | return $obj->isDescendantOf($this); 257 | } 258 | 259 | function isDescendantOf($obj) { 260 | 261 | if($this === $obj) return false; 262 | 263 | $parent = $this; 264 | 265 | while($parent = $parent->parent()) { 266 | if($parent === $obj) return true; 267 | } 268 | 269 | return false; 270 | 271 | } 272 | 273 | function isDescendantOfActive() { 274 | 275 | global $site; 276 | 277 | $active = $site->pages()->active(); 278 | 279 | return $this->isDescendantOf($active); 280 | 281 | } 282 | 283 | function files() { 284 | if($this->files) return $this->files; 285 | $this->files = new files(); 286 | $this->files->init($this); 287 | return $this->files; 288 | } 289 | 290 | function hasFiles() { 291 | return ($this->files()->count() > 0) ? true : false; 292 | } 293 | 294 | function images() { 295 | return $this->files()->images(); 296 | } 297 | 298 | function hasImages() { 299 | return ($this->images()->count() > 0) ? true : false; 300 | } 301 | 302 | function videos() { 303 | return $this->files()->videos(); 304 | } 305 | 306 | function hasVideos() { 307 | return ($this->videos()->count() > 0) ? true : false; 308 | } 309 | 310 | function documents() { 311 | return $this->files()->documents(); 312 | } 313 | 314 | function hasDocuments() { 315 | return ($this->documents()->count() > 0) ? true : false; 316 | } 317 | 318 | function sounds() { 319 | return $this->files()->sounds(); 320 | } 321 | 322 | function hasSounds() { 323 | return ($this->sounds()->count() > 0) ? true : false; 324 | } 325 | 326 | function contents() { 327 | return $this->files()->contents(); 328 | } 329 | 330 | function hasContents() { 331 | return ($this->contents()->count() > 0) ? true : false; 332 | } 333 | 334 | static function fromDir($dir, $parent) { 335 | 336 | // create a new page for this dir 337 | $page = new page(); 338 | $parent = ($parent) ? $parent : new obj(); 339 | 340 | $name = self::parseName($dir['name']); 341 | 342 | // apply all variables 343 | $page->parent = $parent; 344 | $page->num = $name['num']; 345 | $page->uid = $name['uid']; 346 | $page->uri = ltrim($parent->uri . '/' . $page->uid, '/'); 347 | $page->dirname = $dir['name']; 348 | $page->modified = $dir['modified']; 349 | $page->root = $dir['root']; 350 | $page->diruri = self::parseDirURI($dir['root']); 351 | $page->rawfiles = $dir['files']; 352 | $page->children = $dir['children']; 353 | $page->visible = empty($name['num']) ? false : true; 354 | 355 | // create a default title. we always need a title! 356 | $page->title = new variable($name['uid'], $page); 357 | 358 | // gather all files 359 | $page->files(); 360 | 361 | // fetch the content 362 | $content = $page->files()->contents(); 363 | 364 | if(c::get('lang.support')) { 365 | 366 | $fallback = $content->filterBy('languageCode', c::get('lang.default'))->first(); 367 | if(!$fallback) $fallback = $content->first(); 368 | 369 | // get the fallback variables 370 | $variables = ($fallback) ? $fallback->variables : array(); 371 | 372 | $page->intendedTemplate = ($fallback) ? $fallback->template : false; 373 | 374 | if(c::get('lang.translated')) { 375 | 376 | // don't use url_key as fallback 377 | // the fallback should always be the folder name 378 | unset($variables['url_key']); 379 | 380 | $translation = $content->filterBy('languageCode', c::get('lang.current'))->first(); 381 | $variables = ($translation) ? array_merge($variables, $translation->variables) : $variables; 382 | } 383 | 384 | } else { 385 | 386 | $contentfile = $content->first(); 387 | $variables = ($contentfile) ? $contentfile->variables : array(); 388 | 389 | $page->intendedTemplate = ($contentfile) ? $contentfile->template : false; 390 | 391 | } 392 | 393 | // merge all variables 394 | foreach($variables as $key => $var) { 395 | $page->_[$key] = new variable($var, $page); 396 | } 397 | 398 | // multi-language translatable urls 399 | if(c::get('lang.support') && $page->url_key != '') { 400 | $page->translatedUID = $page->url_key(); 401 | $page->translatedURI = ltrim($parent->translatedURI . '/' . $page->url_key(), '/'); 402 | } else { 403 | $page->translatedUID = $page->uid; 404 | $page->translatedURI = ltrim($parent->translatedURI . '/' . $page->uid, '/'); 405 | } 406 | 407 | // attach a cached version of the default content 408 | // for backwards compatibility 409 | $page->content = $page->content(); 410 | 411 | return $page; 412 | 413 | } 414 | 415 | static function parseName($name) { 416 | 417 | if(str::contains($name, '-')) { 418 | $match = str::match($name, '!^([0-9]+[\-]+)!', 0); 419 | $uid = str_replace($match, '', $name); 420 | $num = trim(rtrim($match, '-')); 421 | } else { 422 | $num = false; 423 | $uid = $name; 424 | } 425 | 426 | return array('uid' => $uid, 'num' => $num); 427 | 428 | } 429 | 430 | static function parseDirURI($root) { 431 | 432 | if(c::get('root') == '/') { 433 | $base = ltrim($root, '/'); 434 | } else { 435 | $base = ltrim(str_replace(c::get('root'), '', $root), '/'); 436 | } 437 | 438 | return $base; 439 | } 440 | 441 | } 442 | 443 | class pages extends obj { 444 | 445 | var $index = array(); 446 | var $pagination = null; 447 | var $active = false; 448 | 449 | function __construct($array) { 450 | $_ = array(); 451 | foreach($array as $key => $value) { 452 | $_['_' . $this->_key($key)] = $value; 453 | } 454 | $this->_ = $_; 455 | } 456 | 457 | function __toString() { 458 | $output = array(); 459 | foreach($this->_ as $key => $page) { 460 | $output[] = $page . '
'; 461 | } 462 | return implode("\n", $output); 463 | } 464 | 465 | function _key($key) { 466 | return ltrim($key, '_'); 467 | } 468 | 469 | function index($obj=null, $path=false) { 470 | 471 | if(!$obj) $obj = $this; 472 | 473 | foreach($obj->_ as $key => $page) { 474 | $newPath = ltrim($path . '/' . $page->uid() , '/'); 475 | $this->index[$newPath] = $page; 476 | $this->index($page->children(), $newPath); 477 | } 478 | 479 | return $this->index; 480 | 481 | } 482 | 483 | function find() { 484 | 485 | $args = func_get_args(); 486 | 487 | // find multiple pages 488 | if(count($args) > 1) { 489 | $result = array(); 490 | foreach($args as $arg) { 491 | $page = $this->find($arg); 492 | if($page) $result[$page->uid] = $page; 493 | } 494 | return (empty($result)) ? false : new pages($result); 495 | } 496 | 497 | // find a single page 498 | $path = a::first($args); 499 | $array = str::split($path, '/'); 500 | $obj = $this; 501 | $page = false; 502 | 503 | // check if we need to search for translated urls 504 | $translated = c::get('lang.support'); 505 | 506 | foreach($array as $p) { 507 | 508 | $next = ($translated) ? $obj->findBy('translatedUID', $p) : $obj->{'_' . $p}; 509 | if(!$next) return $page; 510 | 511 | $page = $next; 512 | $obj = $next->children(); 513 | } 514 | 515 | return $page; 516 | } 517 | 518 | function active() { 519 | 520 | global $site; 521 | 522 | if($this->active) return $this->active; 523 | 524 | $uri = (string)$site->uri->path(); 525 | 526 | if(empty($uri)) $uri = c::get('home'); 527 | 528 | $page = $this->find($uri); 529 | 530 | if($page) { 531 | $pageURI = (c::get('lang.support')) ? $page->translatedURI() : $page->uri(); 532 | } else { 533 | $pageURI = c::get('404'); 534 | } 535 | 536 | if(!$page || $pageURI != $uri) { 537 | $page = $site->pages->find(c::get('404')); 538 | } 539 | 540 | return $this->active = $page; 541 | 542 | } 543 | 544 | function findOpen() { 545 | foreach($this->_ as $key => $page) { 546 | if($page->isOpen()) return $page; 547 | } 548 | } 549 | 550 | function findBy($key, $value) { 551 | if(is_array($value)) { 552 | $result = array(); 553 | foreach($value as $arg) { 554 | $page = $this->findBy($key, $arg); 555 | if($page) $result[$page->uid] = $page; 556 | } 557 | return (empty($result)) ? false : new pages($result); 558 | } 559 | if(empty($this->index)) $this->index(); 560 | foreach($this->index as $page) { 561 | if($value == $page->$key()) return $page; 562 | } 563 | return false; 564 | } 565 | 566 | function findByUID() { 567 | $args = func_get_args(); 568 | return $this->findBy('uid', $args); 569 | } 570 | 571 | function findByDirname() { 572 | $args = func_get_args(); 573 | return $this->findBy('dirname', $args); 574 | } 575 | 576 | function findByTitle() { 577 | $args = func_get_args(); 578 | return $this->findBy('title', $args); 579 | } 580 | 581 | function findByHash() { 582 | $args = func_get_args(); 583 | return $this->findBy('hash', $args); 584 | } 585 | 586 | function filterBy() { 587 | 588 | $args = func_get_args(); 589 | $field = a::get($args, 0); 590 | $operator = '=='; 591 | $value = a::get($args, 1); 592 | $split = a::get($args, 2); 593 | 594 | if($value === '!=' || $value === '==' || $value === '*=') { 595 | $operator = $value; 596 | $value = a::get($args, 2); 597 | $split = a::get($args, 3); 598 | } 599 | 600 | $pages = array(); 601 | 602 | switch($operator) { 603 | 604 | // ignore matching elements 605 | case '!=': 606 | 607 | foreach($this->_ as $key => $page) { 608 | if($split) { 609 | $values = str::split((string)$page->$field(), $split); 610 | if(!in_array($value, $values)) $pages[$key] = $page; 611 | } else if($page->$field() != $value) { 612 | $pages[$key] = $page; 613 | } 614 | } 615 | break; 616 | 617 | // search 618 | case '*=': 619 | 620 | foreach($this->_ as $key => $page) { 621 | if($split) { 622 | $values = str::split((string)$page->$field(), $split); 623 | foreach($values as $val) { 624 | if(str::contains($val, $value)) { 625 | $pages[$key] = $page; 626 | break; 627 | } 628 | } 629 | } else if(str::contains($page->$field(), $value)) { 630 | $pages[$key] = $page; 631 | } 632 | } 633 | 634 | // take all matching elements 635 | default: 636 | 637 | foreach($this->_ as $key => $page) { 638 | if($split) { 639 | $values = str::split((string)$page->$field(), $split); 640 | if(in_array($value, $values)) $pages[$key] = $page; 641 | } else if($page->$field() == $value) { 642 | $pages[$key] = $page; 643 | } 644 | } 645 | 646 | break; 647 | 648 | } 649 | 650 | return new pages($pages); 651 | } 652 | 653 | function visible() { 654 | return $this->filterBy('visible', true); 655 | } 656 | 657 | function countVisible() { 658 | return $this->visible()->count(); 659 | } 660 | 661 | function invisible() { 662 | return $this->filterBy('visible', false); 663 | } 664 | 665 | function countInvisible() { 666 | return $this->invisible()->count(); 667 | } 668 | 669 | function without($uid) { 670 | $pages = $this->_; 671 | unset($pages['_' . $uid]); 672 | return new pages($pages); 673 | } 674 | 675 | function not($uid) { 676 | return $this->without($uid); 677 | } 678 | 679 | function flip() { 680 | $pages = array_reverse($this->_, true); 681 | return new pages($pages); 682 | } 683 | 684 | function slice($offset=null, $limit=null) { 685 | if($offset === null && $limit === null) return $this; 686 | return new pages(array_slice($this->_, $offset, $limit)); 687 | } 688 | 689 | function limit($limit) { 690 | return $this->slice(0, $limit); 691 | } 692 | 693 | function offset($offset) { 694 | return $this->slice($offset); 695 | } 696 | 697 | function sortBy($field, $direction='asc', $method=SORT_REGULAR) { 698 | 699 | if($field == 'dirname') { 700 | $method = 'natural'; 701 | } 702 | 703 | $pages = a::sort($this->_, $field, $direction, $method); 704 | return new pages($pages); 705 | 706 | } 707 | 708 | function paginate($limit, $options=array()) { 709 | 710 | $pagination = new pagination($this, $limit, $options); 711 | $pages = $this->slice($pagination->offset, $pagination->limit); 712 | $pages->pagination = $pagination; 713 | 714 | return $pages; 715 | 716 | } 717 | 718 | function pagination() { 719 | return $this->pagination; 720 | } 721 | 722 | function children() { 723 | $result = array(); 724 | foreach($this->_ as $page) { 725 | foreach($page->children() as $key => $child) { 726 | $result[$key] = $child; 727 | } 728 | } 729 | return new pages($result); 730 | } 731 | 732 | static function merge() { 733 | 734 | $objs = func_get_args(); 735 | $result = array(); 736 | 737 | foreach($objs as $obj) { 738 | foreach($obj as $key => $page) { 739 | $result[$key] = $page; 740 | } 741 | } 742 | 743 | return new pages($result); 744 | 745 | } 746 | 747 | } 748 | 749 | class pagecontent extends obj { 750 | 751 | function __toString() { 752 | return $this->filecontent; 753 | } 754 | 755 | } 756 | -------------------------------------------------------------------------------- /kirby/lib/pagination.php: -------------------------------------------------------------------------------- 1 | pagevar = c::get('pagination.variable', 'page'); 37 | $this->mode = a::get($options, 'mode', c::get('pagination.method', 'params')) == 'query' ? 'query' : 'params'; 38 | $this->data = $data; 39 | $this->count = $data->count(); 40 | $this->limit = $limit; 41 | $this->page = ($this->mode == 'query') ? intval(get($this->pagevar)) : intval($site->uri->param($this->pagevar)); 42 | $this->pages = ceil($this->count / $this->limit); 43 | 44 | // sanitize the page 45 | if($this->page < 1) $this->page = 1; 46 | 47 | if($this->page > $this->pages && $this->count > 0) go($this->firstPageURL()); 48 | 49 | // generate the offset 50 | $this->offset = ($this->page-1)*$this->limit; 51 | 52 | } 53 | 54 | function page() { 55 | return $this->page; 56 | } 57 | 58 | function countPages() { 59 | return $this->pages; 60 | } 61 | 62 | function hasPages() { 63 | return ($this->countPages() > 1) ? true : false; 64 | } 65 | 66 | function countItems() { 67 | return $this->data->count(); 68 | } 69 | 70 | function pageURL($page) { 71 | global $site; 72 | ($this->mode == 'query') ? $site->uri->replaceQueryKey($this->pagevar, $page) : $site->uri->replaceParam($this->pagevar, $page); 73 | return $site->uri->toUrl(); 74 | } 75 | 76 | function firstPage() { 77 | return 1; 78 | } 79 | 80 | function isFirstPage() { 81 | return ($this->page == $this->firstPage()) ? true : false; 82 | } 83 | 84 | function firstPageURL() { 85 | return $this->pageURL(1); 86 | } 87 | 88 | function lastPage() { 89 | return $this->pages; 90 | } 91 | 92 | function isLastPage() { 93 | return ($this->page == $this->lastPage()) ? true : false; 94 | } 95 | 96 | function lastPageURL() { 97 | return $this->pageURL($this->lastPage()); 98 | } 99 | 100 | function prevPage() { 101 | return ($this->hasPrevPage()) ? $this->page-1 : $this->page; 102 | } 103 | 104 | function prevPageURL() { 105 | return $this->pageURL($this->prevPage()); 106 | } 107 | 108 | function hasPrevPage() { 109 | return ($this->page <= 1) ? false : true; 110 | } 111 | 112 | function nextPage() { 113 | return ($this->hasNextPage()) ? $this->page+1 : $this->page; 114 | } 115 | 116 | function nextPageURL() { 117 | return $this->pageURL($this->nextPage()); 118 | } 119 | 120 | function hasNextPage() { 121 | return ($this->page >= $this->pages) ? false : true; 122 | } 123 | 124 | function numStart() { 125 | return $this->offset+1; 126 | } 127 | 128 | function numEnd() { 129 | return $this->offset+$this->limit; 130 | } 131 | 132 | function range($range=5) { 133 | 134 | if($this->countPages() <= $range) { 135 | $this->rangeStart = 1; 136 | $this->rangeEnd = $this->countPages(); 137 | return range($this->rangeStart, $this->rangeEnd); 138 | } 139 | 140 | $this->rangeStart = $this->page - floor($range/2); 141 | $this->rangeEnd = $this->page + floor($range/2); 142 | 143 | if($this->rangeStart <= 0) { 144 | $this->rangeEnd += abs($this->rangeStart)+1; 145 | $this->rangeStart = 1; 146 | } 147 | 148 | if($this->rangeEnd > $this->countPages()) { 149 | $this->rangeStart -= $this->rangeEnd-$this->countPages(); 150 | $this->rangeEnd = $this->countPages(); 151 | } 152 | 153 | return range($this->rangeStart,$this->rangeEnd); 154 | 155 | } 156 | 157 | function rangeStart() { 158 | return $this->rangeStart; 159 | } 160 | 161 | function rangeEnd() { 162 | return $this->rangeEnd; 163 | } 164 | 165 | } 166 | 167 | -------------------------------------------------------------------------------- /kirby/lib/site.php: -------------------------------------------------------------------------------- 1 | urlSetup(); 16 | $this->languageSetup(); 17 | 18 | // check if the cache is enabled at all 19 | $this->cacheEnabled = (c::get('cache') && (c::get('cache.html') || c::get('cache.data'))) ? true : false; 20 | 21 | if($this->cacheEnabled) { 22 | 23 | $this->dataCacheEnabled = c::get('cache.data'); 24 | $this->htmlCacheEnabled = c::get('cache.html'); 25 | 26 | if(c::get('cache.autoupdate')) { 27 | $this->modified = dir::modified(c::get('root.content')); 28 | } else { 29 | $this->modified = 0; 30 | } 31 | 32 | } 33 | 34 | $cacheID = $this->dataCacheID(); 35 | $cacheModified = time(); 36 | $cacheData = null; 37 | 38 | // data cache 39 | if($this->dataCacheEnabled) { 40 | 41 | // find the latest modifed date from all content subdirs 42 | // if the cache is enabled and autoupdate is activated. 43 | // otherwise the last modified date will be false so the cache 44 | // will stay valid forever 45 | 46 | // check when the data cache has been modified 47 | $cacheModified = cache::modified($cacheID); 48 | 49 | // check if the cache is still valid 50 | if($cacheModified >= $this->modified) { 51 | $cacheData = cache::get($cacheID); 52 | } 53 | 54 | } 55 | 56 | if(empty($cacheData)) { 57 | 58 | // get the first set of pages 59 | $this->rootPages(); 60 | // get the additional site info from content/site.txt 61 | $this->info(); 62 | 63 | if($this->dataCacheEnabled) cache::set($cacheID, $this->_); 64 | 65 | } else { 66 | $this->_ = $cacheData; 67 | } 68 | 69 | // attach the uri after caching 70 | $this->uri = new uri(); 71 | 72 | } 73 | 74 | function __toString() { 75 | return '' . $this->url() . ''; 76 | } 77 | 78 | function load() { 79 | 80 | // initiate the site and make pages and page 81 | // globally available 82 | $pages = $this->pages; 83 | $page = $this->pages->active(); 84 | 85 | // check for ssl 86 | if(c::get('ssl')) { 87 | // if there's no https in the url 88 | if(!server::get('https')) go(str_replace('http://', 'https://', $page->url())); 89 | } 90 | 91 | // check for index.php in rewritten urls and rewrite them 92 | if(c::get('rewrite') && preg_match('!index.php\/!i', $this->uri->original)) { 93 | go($page->url()); 94 | } 95 | 96 | // check for a misconfigured subfolder install 97 | if($page->isErrorPage()) { 98 | 99 | // if you want to store subfolders in the homefolder for blog articles i.e. and you 100 | // want urls like http://yourdomain.com/article-title you can set 101 | // RedirectMatch 301 ^/home/(.*)$ /$1 in your htaccess file and those 102 | // next lines will take care of delivering the right pages. 103 | $uri = c::get('home') . '/' . $this->uri->path(); 104 | 105 | if($redirected = $this->pages()->find($uri)) { 106 | if($redirected->uri() == $uri) { 107 | $page = $redirected; 108 | $this->pages->active = $page; 109 | $this->uri = new uri($uri); 110 | } 111 | } 112 | 113 | // try to rewrite broken translated urls 114 | // this will only work for default uris 115 | if(c::get('lang.support')) { 116 | 117 | $path = $this->uri->path->toArray(); 118 | $obj = $pages; 119 | $found = false; 120 | 121 | foreach($path as $p) { 122 | 123 | // first try to find the page by uid 124 | $next = $obj->{'_' . $p}; 125 | 126 | if(!$next) { 127 | 128 | // go through each translation for each child page 129 | // and try to find the url_key or uid there 130 | foreach($obj as $child) { 131 | foreach(c::get('lang.available') as $lang) { 132 | $c = $child->content($lang); 133 | // redirect to the url if a translated url has been found 134 | if($c && $c->url_key() == $p && !$child->isErrorPage()) $next = $child; 135 | } 136 | } 137 | 138 | if(!$next) break; 139 | } 140 | 141 | $found = $next; 142 | $obj = $next->children(); 143 | } 144 | 145 | if($found && !$found->isErrorPage()) go($found->url()); 146 | 147 | } 148 | 149 | } 150 | 151 | // redirect file urls (file:image.jpg) 152 | if($this->uri->param('file')) { 153 | // get the local file 154 | $file = $page->files()->find($this->uri->param('file')); 155 | if($file) go($file->url()); 156 | } 157 | 158 | // redirect /home to / 159 | if($this->uri->path() == c::get('home')) go(url()); 160 | 161 | // redirect tinyurls 162 | if($this->uri->path(1) == c::get('tinyurl.folder') && c::get('tinyurl.enabled')) { 163 | $hash = $this->uri->path(2); 164 | 165 | if(!empty($hash)) { 166 | $resolved = $this->pages->findByHash($hash)->first(); 167 | // redirect to the original page 168 | if($resolved) go(url($resolved->uri)); 169 | } 170 | 171 | } 172 | 173 | // set the global template vars 174 | tpl::set('site', $this); 175 | tpl::set('pages', $pages); 176 | tpl::set('page', $page); 177 | 178 | $cacheID = $this->htmlCacheID(); 179 | $cacheModified = time(); 180 | $cacheData = null; 181 | 182 | if($this->htmlCacheEnabled) { 183 | 184 | // check if the cache is disabled for some reason 185 | $this->htmlCacheEnabled = ($page->isErrorPage() || in_array($page->uri(), c::get('cache.ignore', array()))) ? false : true; 186 | 187 | // check for the last modified date of the cache file 188 | $cacheModified = cache::modified($cacheID); 189 | 190 | // check if the files have been modified 191 | // since the last html cache file has been written 192 | if($this->htmlCacheEnabled && $cacheModified >= $this->modified) { 193 | $cacheData = cache::get($cacheID, true); 194 | } 195 | 196 | } 197 | 198 | // send a 404 header if this is the error page 199 | if($page->isErrorPage() && c::get('404.header')) header("HTTP/1.0 404 Not Found"); 200 | 201 | if(empty($cacheData)) { 202 | // load the main template 203 | $html = tpl::load($page->template(), array(), true); 204 | if($this->htmlCacheEnabled) cache::set($cacheID, (string)$html, true); 205 | } else { 206 | $html = $cacheData; 207 | } 208 | 209 | die($html); 210 | 211 | } 212 | 213 | function breadcrumb() { 214 | 215 | if($this->breadcrumb) return $this->breadcrumb; 216 | 217 | $uri = $this->uri->path->toArray(); 218 | $crumb = array(); 219 | 220 | foreach($uri AS $u) { 221 | $tmp = implode('/', $uri); 222 | $data = $this->pages->find($tmp); 223 | 224 | if(!$data || $data->isErrorPage()) { 225 | // add the error page to the crumb 226 | $crumb[] = $this->pages->find(c::get('404')); 227 | // don't move on with subpages, because there won't be 228 | // any if the first page hasn't been found at all 229 | break; 230 | } else { 231 | $crumb[] = $data; 232 | } 233 | array_pop($uri); 234 | } 235 | 236 | // we've been moving through the uri from tail to head 237 | // so we need to reverse the array to get a proper crumb 238 | $crumb = array_reverse($crumb); 239 | 240 | // add the homepage to the beginning of the crumb array 241 | array_unshift($crumb, $this->pages->find(c::get('home'))); 242 | 243 | // make it a pages object so we can handle it 244 | // like we handle all pages on the site 245 | return $this->breadcrumb = new pages($crumb); 246 | 247 | } 248 | 249 | function url($lang=false) { 250 | $url = c::get('url'); 251 | return ($lang && c::get('lang.support') && in_array($lang, c::get('lang.available', array()))) ? url(false, $lang) : $url; 252 | } 253 | 254 | function serialize() { 255 | return serialize($this); 256 | } 257 | 258 | function rootPages() { 259 | 260 | // get the first level in the content root 261 | $ignore = array_merge(array('.svn', '.git', '.htaccess'), (array)c::get('content.file.ignore', array())); 262 | $files = dir::inspect(c::get('root.content'), $ignore); 263 | $pages = array(); 264 | 265 | // build the first set of pages 266 | foreach($files['children'] as $file) { 267 | 268 | $child = dir::inspect($files['root'] . '/' . $file, $ignore); 269 | $page = page::fromDir($child, false); 270 | 271 | // add false as parent page object because we are on the first level 272 | $page->parent = false; 273 | 274 | $pages[$page->uid] = $page; 275 | 276 | } 277 | 278 | $this->pages = new pages($pages); 279 | 280 | } 281 | 282 | function info($lang=false) { 283 | 284 | // first run: fetch all the things we need 285 | if(!$this->info) { 286 | 287 | $root = c::get('root.content'); 288 | 289 | if(c::get('lang.support')) { 290 | 291 | $defaultLang = c::get('lang.default'); 292 | $currentLang = c::get('lang.current'); 293 | 294 | foreach(c::get('lang.available') as $lang) { 295 | $file = $root . '/site.' . $lang . '.' . c::get('content.file.extension', 'txt'); 296 | if(!file_exists($file)) continue; 297 | 298 | // fetch the site info from the defaulf file. 299 | $fetched = variables::fetch($file); 300 | $data = $fetched['data']; 301 | $data['filecontent'] = $fetched['raw']; 302 | 303 | $this->_['info'][$lang] = $data; 304 | 305 | } 306 | 307 | // if there's no default language 308 | if(!isset($this->_['info'][$defaultLang])) { 309 | 310 | $file = $root . '/site.' . c::get('content.file.extension', 'txt'); 311 | 312 | if(file_exists($file)) { 313 | 314 | // fetch the site info from the defaulf file. 315 | $fetched = variables::fetch($file); 316 | $data = $fetched['data']; 317 | $data['filecontent'] = $fetched['raw']; 318 | 319 | $this->_['info'][$defaultLang] = $data; 320 | 321 | } else { 322 | $this->_['info'][$defaultLang] = array(); 323 | } 324 | 325 | } 326 | 327 | foreach($this->_['info'] as $key => $value) { 328 | if($key == $defaultLang) continue; 329 | 330 | $merged = array_merge($this->_['info'][$defaultLang], $value); 331 | $this->_['info'][$key] = new siteinfo($merged); 332 | } 333 | 334 | // bake the default language stuff into an object finally 335 | $this->_['info'][$defaultLang] = new siteinfo($this->_['info'][$defaultLang]); 336 | 337 | // get the current variables 338 | $current = (isset($this->_['info'][$currentLang])) ? $this->_['info'][$currentLang] : $this->_['info'][$defaultLang]; 339 | 340 | } else { 341 | 342 | $file = $root . '/site.' . c::get('content.file.extension', 'txt'); 343 | 344 | if(file_exists($file)) { 345 | 346 | // fetch the site info from the defaulf file. 347 | $fetched = variables::fetch($file); 348 | $data = $fetched['data']; 349 | $data['filecontent'] = $fetched['raw']; 350 | 351 | $this->_['info'] = new siteinfo($data); 352 | } else { 353 | $this->_['info'] = new siteinfo(array()); 354 | } 355 | 356 | $current = $this->_['info']; 357 | 358 | } 359 | 360 | // add all variables 361 | $vars = $current->_; 362 | 363 | // don't add the filecontent var, 364 | // because this is not a custom var 365 | unset($vars['filecontent']); 366 | 367 | $this->variables = $vars; 368 | 369 | // merge the current site info with the additional 370 | // info from the info file(s) 371 | $this->_ = array_merge($this->_, $current->_); 372 | 373 | } 374 | 375 | // now get the stuff the user wants 376 | if(c::get('lang.support')) { 377 | 378 | $currentLang = c::get('lang.current'); 379 | $defaultLang = c::get('lang.default'); 380 | 381 | if($lang && in_array($lang, c::get('lang.available'))) { 382 | return (isset($this->info[$lang])) ? $this->info[$lang] : $this->info[$defaultLang]; 383 | } 384 | 385 | return (isset($this->info[$currentLang])) ? $this->info[$currentLang] : $this->info[$defaultLang]; 386 | 387 | } else { 388 | return $this->info; 389 | } 390 | 391 | } 392 | 393 | function modified($format=false) { 394 | return ($this->modified) ? ($format ? date($format, $this->modified) : $this->modified) : ($format ? date($format) : time()); 395 | } 396 | 397 | function dataCacheID() { 398 | return (c::get('lang.support')) ? 'site.' . c::get('lang.current') . '.php' : 'site.php'; 399 | } 400 | 401 | function htmlCacheID() { 402 | return (c::get('lang.support')) ? $this->uri->toCacheID() . '.' . c::get('lang.current') . '.php' : $this->uri->toCacheID() . '.php'; 403 | } 404 | 405 | function languageSetup() { 406 | 407 | // check for activated language support 408 | if(!c::get('lang.support')) return false; 409 | 410 | // get the available languages 411 | $available = c::get('lang.available'); 412 | 413 | // sanitize the available languages 414 | if(!is_array($available)) { 415 | 416 | // switch off language support 417 | c::set('lang.support', false); 418 | return false; 419 | 420 | } 421 | 422 | // get the raw uri 423 | $uri = uri::raw(); 424 | 425 | // get the current language code 426 | $code = a::first(explode('/', $uri)); 427 | 428 | // try to detect the language code if the code is empty 429 | if(empty($code)) { 430 | 431 | if(c::get('lang.detect')) { 432 | // detect the current language 433 | $detected = str::split(server::get('http_accept_language'), ','); 434 | $detected = a::first($detected); 435 | $detected = str::split($detected, '-'); 436 | $detected = a::first($detected); 437 | $detected = (!in_array($detected, $available)) ? c::get('lang.default') : $detected; 438 | 439 | // set the detected code as current code 440 | $code = $detected; 441 | 442 | } else { 443 | $code = c::get('lang.default'); 444 | } 445 | 446 | // go to the default homepage 447 | go(url(false, $code)); 448 | 449 | } 450 | 451 | // http://yourdomain.com/error 452 | // will redirect to http://yourdomain.com/en/error 453 | if($code == c::get('404')) go(url('error', c::get('lang.default'))); 454 | 455 | // validate the code and switch back to the homepage if it is invalid 456 | if(!in_array($code, c::get('lang.available'))) go(url()); 457 | 458 | // set the current language 459 | c::set('lang.current', $code); 460 | 461 | // mark if this is a translated version or the default version 462 | ($code != c::get('lang.default')) ? c::set('lang.translated', true) : c::set('lang.translated', false); 463 | 464 | // load the additional language files if available 465 | load::language(); 466 | 467 | } 468 | 469 | function urlSetup() { 470 | 471 | // auto-detect the url if it is not set 472 | $url = (c::get('url') === false) ? c::get('scheme') . server::get('http_host') : rtrim(c::get('url'), '/'); 473 | 474 | // try to detect the subfolder 475 | $subfolder = (c::get('subfolder')) ? trim(c::get('subfolder'), '/') : trim(dirname($_SERVER['SCRIPT_NAME']), '/\\'); 476 | 477 | if($subfolder) { 478 | c::set('subfolder', $subfolder); 479 | 480 | // check if the url already contains the subfolder 481 | // so it's not included twice 482 | if(!preg_match('!' . preg_quote($subfolder) . '$!i', $url)) $url .= '/' . $subfolder; 483 | } 484 | 485 | // set the final url 486 | c::set('url', $url); 487 | 488 | } 489 | 490 | function hasPlugin($plugin) { 491 | return (file_exists(c::get('root.plugins') . '/' . $plugin . '.php') || file_exists(c::get('root.plugins') . '/' . $plugin . '/' . $plugin . '.php')) ? true : false; 492 | } 493 | 494 | } 495 | 496 | class siteinfo extends obj { 497 | 498 | function __toString() { 499 | return $this->filecontent; 500 | } 501 | 502 | } 503 | -------------------------------------------------------------------------------- /kirby/lib/template.php: -------------------------------------------------------------------------------- 1 | path = new uriPath(); 18 | $this->params = new uriParams(); 19 | $this->query = new uriQuery(str::parse(server::get('query_string'), 'query')); 20 | $this->extension = false; 21 | $this->original = $_SERVER['REQUEST_URI']; 22 | $this->raw = $this->raw($uri); 23 | $this->url = url(ltrim($this->raw, '/')); 24 | 25 | // crawl the uri and get all elements 26 | $this->crawl(); 27 | 28 | } 29 | 30 | function __toString() { 31 | return $this->toString(); 32 | } 33 | 34 | static function raw($uri=false) { 35 | $raw = ($uri) ? $uri : ltrim($_SERVER['REQUEST_URI'], '/'); 36 | $raw = ltrim(str_replace('index.php', '', $raw), '/'); 37 | 38 | // strip subfolders from uri 39 | if(c::get('subfolder')) $raw = ltrim(preg_replace('!^' . preg_quote(c::get('subfolder')) . '(\/|)!i', '/', $raw), '/'); 40 | if(c::get('lang.support')) $raw = ltrim(preg_replace('!^' . preg_quote(c::get('lang.current')) . '(\/|)!i', '/', $raw), '/'); 41 | 42 | return $raw; 43 | } 44 | 45 | function crawl() { 46 | 47 | $path = url::strip_query($this->raw); 48 | $path = (array)str::split($path, '/'); 49 | if(a::first($path) == 'index.php') array_shift($path); 50 | 51 | // parse params 52 | foreach($path AS $p) { 53 | if(str::contains($p, c::get('uri.param.separator'))) { 54 | $parts = explode(c::get('uri.param.separator'), $p); 55 | if(count($parts) < 2) continue; 56 | $this->params->$parts[0] = $parts[1]; 57 | } else { 58 | $this->path->_[] = $p; 59 | } 60 | } 61 | 62 | // get the extension from the last part of the path 63 | $this->extension = f::extension($this->path->last()); 64 | 65 | if($this->extension != false) { 66 | // remove the last part of the path 67 | $last = array_pop($this->path->_); 68 | $this->path->_[] = f::name($last); 69 | } 70 | 71 | return $this->path; 72 | 73 | } 74 | 75 | function path($key=false, $default=false) { 76 | if(!$key) return $this->path; 77 | return $this->path->find($key, $default); 78 | } 79 | 80 | function param($key=false, $default=false) { 81 | if(!$key) return $this->params; 82 | return $this->params->find($key, $default); 83 | } 84 | 85 | function params($key=false, $default=false) { 86 | return $this->param($key, $default); 87 | } 88 | 89 | function query($key=false, $default=false) { 90 | if(!$key) return $this->query; 91 | return $this->query->find($key, $default); 92 | } 93 | 94 | function toString($includeQuery=true) { 95 | 96 | $parts = array(); 97 | $path = $this->path(); 98 | $params = $this->params(); 99 | $query = $this->query(); 100 | 101 | if(!empty($path->_)) $parts[] = (string)$path; 102 | if(!empty($params->_)) $parts[] = (string)$params; 103 | 104 | if($includeQuery && !empty($query->_)) $parts[] = '?' . $query; 105 | 106 | return implode('/', $parts); 107 | 108 | } 109 | 110 | function toUrl($includeQuery=true) { 111 | return url($this->toString($includeQuery)); 112 | } 113 | 114 | function toCacheID() { 115 | $url = $this->toURL(); 116 | return md5($url); 117 | } 118 | 119 | function stripPath() { 120 | $this->path = new uriPath(); 121 | return $this; 122 | } 123 | 124 | function replaceParam($key, $value) { 125 | $this->params->{$key} = $value; 126 | return $this; 127 | } 128 | 129 | function removeParam($key) { 130 | unset($this->params->_[$key]); 131 | return $this; 132 | } 133 | 134 | function stripParams() { 135 | $this->params = new uriParams(); 136 | return $this; 137 | } 138 | 139 | function urlWithoutParam($param) { 140 | $this->removeParam($param); 141 | return $this->toUrl(); 142 | } 143 | 144 | function replaceQueryKey($key, $value) { 145 | $this->query->{$key} = $value; 146 | return $this; 147 | } 148 | 149 | function removeQueryKey($key) { 150 | unset($this->query->_[$key]); 151 | return $this; 152 | } 153 | 154 | function urlWithoutQueryKey($key) { 155 | $this->removeQueryKey($key); 156 | return $this->toUrl(); 157 | } 158 | 159 | function stripQuery() { 160 | $this->query = new uriQuery(); 161 | return $this; 162 | } 163 | 164 | } 165 | 166 | class uriPath extends obj { 167 | 168 | function __toString() { 169 | return $this->toString(); 170 | } 171 | 172 | function toString() { 173 | return implode('/', $this->_); 174 | } 175 | 176 | function find($key=false, $default=false) { 177 | if($key===false) return $this->_; 178 | $key--; 179 | return a::get($this->_, $key, $default); 180 | } 181 | 182 | } 183 | 184 | class uriParams extends obj { 185 | 186 | function __toString() { 187 | return $this->toString(); 188 | } 189 | 190 | function toString() { 191 | $output = array(); 192 | foreach($this->_ as $key => $value) { 193 | $output[] = $key . c::get('uri.param.separator') . $value; 194 | } 195 | return implode('/', $output); 196 | } 197 | 198 | } 199 | 200 | class uriQuery extends obj { 201 | 202 | function __toString() { 203 | return $this->toString(); 204 | } 205 | 206 | function toString() { 207 | return http_build_query($this->_); 208 | } 209 | 210 | } 211 | 212 | -------------------------------------------------------------------------------- /kirby/lib/variables.php: -------------------------------------------------------------------------------- 1 | value = $value; 10 | $this->parent = $parent; 11 | } 12 | 13 | function __toString() { 14 | return (string)$this->value; 15 | } 16 | 17 | } 18 | 19 | class variables extends file { 20 | 21 | function __construct($array) { 22 | 23 | parent::__construct($array); 24 | 25 | $vars = self::fetch($this->root); 26 | $this->_['variables'] = array(); 27 | $this->filecontent = @$vars['raw']; 28 | 29 | if($vars && is_array($vars)) { 30 | foreach($vars['data'] as $key => $var) { 31 | $this->_['variables'][$key] = $var; 32 | } 33 | } 34 | 35 | } 36 | 37 | static function fetch($file) { 38 | if(!file_exists($file)) return array( 39 | 'raw' => false, 40 | 'data' => array() 41 | ); 42 | $content = f::read($file); 43 | $content = str_replace("\xEF\xBB\xBF", '', $content); 44 | $sections = preg_split('![\r\n]+[-]{4,}!i', $content); 45 | $data = array(); 46 | foreach($sections AS $s) { 47 | $parts = explode(':', $s); 48 | if(count($parts) == 1 && count($sections) == 1) { 49 | return $content; 50 | } 51 | $key = str::lower(preg_replace('![^a-z0-9]+!i', '_', trim($parts[0]))); 52 | if(empty($key)) continue; 53 | $value = trim(implode(':', array_slice($parts, 1))); 54 | $data[$key] = $value; 55 | } 56 | 57 | return array( 58 | 'raw' => $content, 59 | 'data' => $data, 60 | ); 61 | } 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /kirby/modals/mbstring.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kirby Installation 6 | 7 | 8 | 9 | 49 | 50 | 51 | 52 | 53 | 54 |

Kirby Installation

55 | 56 |
57 |
Missing mbstring functions
58 |
59 | Kirby requires PHP mbstring functions: http://php.net/manual/en/book.mbstring.php.

60 | Please ask your system administrator
or hosting company 61 | to install them for you. 62 |
63 |
64 | If you got further questions, please contact me:
65 | bastian@getkirby.com 66 |
67 |
68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /kirby/modals/troubleshoot.php: -------------------------------------------------------------------------------- 1 | 33 | 34 | 35 | 36 | 37 | Kirby Troubleshooting 38 | 39 | 40 | 41 | 82 | 83 | 84 | 85 | 86 | 87 |

Kirby Troubleshooting

88 | 89 |
90 |
Kirby CMS Version
91 |
92 | 93 |
Kirby Toolkit Version
94 |
95 | 96 |
URL
97 | 98 |
99 | The URL for your site seems to be setup incorrectly
100 | URL in your config:
101 | Detected URL: 102 |
103 | 104 |
105 | 106 | 107 |
Subfolder
108 | 109 |
110 | You might want to set the subfolder in your config file
111 | Subfolder in site/config/config.php:
112 | Detected Subfolder: 113 |
114 | 115 |
Your site seems not to be running in a subfolder
116 | 117 |
Your site seems to be running in a subfolder
118 | 119 | 120 |
Root
121 |
122 | 123 |
System Folder
124 |
125 | 126 |
Content Folder
127 |
128 | 129 |
Site Folder
130 |
131 | 132 |
Templates Folder
133 | 134 |
135 | 136 |
Your templates folder could not be found
137 | 138 | 139 |
Default Template
140 | 141 |
142 | Your default template is missing
143 |
144 | 145 |
146 | 147 | 148 |
Cache Folder
149 | 150 |
Your cache folder could not be found
151 | 152 |
Your cache folder seems not to be writable
153 | 154 |
155 | 156 | 157 |
Cache Data Structure
158 |
159 | 160 |
Cache HTML
161 |
162 | 163 |
URL-Rewriting
164 | 165 |
Can't detect url rewriting. You are probably not running Kirby on Apache. You might need to setup your own rewrite rules depending on your server setup.
166 | 167 |
url rewriting is enabled
168 | 169 |
mod_rewrite seems not to be available
170 | 171 |
url rewriting is disabled
172 | 173 | 174 |
Your PHP Version
175 | 176 |
177 | 178 |
- this version is not compatible!!!
179 | 180 | 181 |
Your Server Software
182 |
183 | 184 |
Installed Plugins
185 |
186 | 187 |
Installed Snippets
188 |
189 | 190 |
Your config files
191 |
192 | 193 |
Your entire config
194 |
195 | 196 |
PHP Error Reporting
197 |
198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /kirby/parsers/defaults.php: -------------------------------------------------------------------------------- 1 | youtube(array( 19 | 'youtube' => $url, 20 | 'width' => $width, 21 | 'height' => $height, 22 | 'class' => $class 23 | )); 24 | } 25 | 26 | function vimeo($url, $width=false, $height=false, $class=false) { 27 | $name = kirbytext::classname(); 28 | $obj = new $name; 29 | return $obj->vimeo(array( 30 | 'vimeo' => $url, 31 | 'width' => $width, 32 | 'height' => $height, 33 | 'class' => $class 34 | )); 35 | } 36 | 37 | function flash($url, $width=false, $height=false) { 38 | $name = kirbytext::classname(); 39 | $obj = new $name; 40 | return $obj->flash($url, $width, $height); 41 | } 42 | 43 | function twitter($username, $text=false, $title=false, $class=false) { 44 | $name = kirbytext::classname(); 45 | $obj = new $name; 46 | return $obj->twitter(array( 47 | 'twitter' => $username, 48 | 'text' => $text, 49 | 'title' => $title, 50 | 'class' => $class 51 | )); 52 | } 53 | 54 | function gist($url, $file=false) { 55 | $name = kirbytext::classname(); 56 | $obj = new $name; 57 | return $obj->gist(array( 58 | 'gist' => $url, 59 | 'file' => $file 60 | )); 61 | } 62 | 63 | class kirbytext { 64 | 65 | var $obj = null; 66 | var $text = null; 67 | var $mdown = true; 68 | var $smartypants = true; 69 | var $tags = array('gist', 'twitter', 'date', 'image', 'file', 'link', 'email', 'youtube', 'vimeo'); 70 | var $attr = array('text', 'file', 'width', 'height', 'link', 'popup', 'class', 'title', 'alt', 'rel', 'lang', 'target', 'download'); 71 | 72 | static function init($text=false, $mdown=true, $smartypants=true) { 73 | 74 | $classname = self::classname(); 75 | $kirbytext = new $classname($text, $mdown, $smartypants); 76 | return $kirbytext->get(); 77 | 78 | } 79 | 80 | function __construct($text=false, $mdown=true, $smartypants=true) { 81 | 82 | $this->text = $text; 83 | $this->mdown = $mdown; 84 | $this->smartypants = $smartypants; 85 | 86 | // pass the parent page if available 87 | if(is_object($this->text)) $this->obj = $this->text->parent; 88 | 89 | } 90 | 91 | function get() { 92 | 93 | $text = preg_replace_callback('!(?=[^\]])\((' . implode('|', $this->tags) . '):(.*?)\)!i', array($this, 'parse'), (string)$this->text); 94 | $text = preg_replace_callback('!```(.*?)```!is', array($this, 'code'), $text); 95 | 96 | $text = ($this->mdown) ? markdown($text) : $text; 97 | $text = ($this->smartypants) ? smartypants($text) : $text; 98 | 99 | return $text; 100 | 101 | } 102 | 103 | function code($code) { 104 | 105 | $code = @$code[1]; 106 | $lines = explode("\n", $code); 107 | $first = trim(array_shift($lines)); 108 | $code = implode("\n", $lines); 109 | $code = trim($code); 110 | 111 | if(function_exists('highlight')) { 112 | $result = '
';
113 |       $result .= '';
114 |       $result .= highlight($code, (empty($first)) ? 'php-html' : $first);
115 |       $result .= '';
116 |       $result .= '
'; 117 | } else { 118 | $result = '
';
119 |       $result .= '';
120 |       $result .= htmlspecialchars($code);
121 |       $result .= '';
122 |       $result .= '
'; 123 | } 124 | 125 | return $result; 126 | 127 | } 128 | 129 | function parse($args) { 130 | 131 | $method = strtolower(@$args[1]); 132 | $string = @$args[0]; 133 | 134 | if(empty($string)) return false; 135 | if(!method_exists($this, $method)) return $string; 136 | 137 | $replace = array('(', ')'); 138 | $string = str_replace($replace, '', $string); 139 | $attr = array_merge($this->tags, $this->attr); 140 | $search = preg_split('!(' . implode('|', $attr) . '):!i', $string, false, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); 141 | $result = array(); 142 | $num = 0; 143 | 144 | foreach($search AS $key) { 145 | 146 | if(!isset($search[$num+1])) break; 147 | 148 | $key = trim($search[$num]); 149 | $value = trim($search[$num+1]); 150 | 151 | $result[ $key ] = $value; 152 | $num = $num+2; 153 | 154 | } 155 | 156 | return $this->$method($result); 157 | 158 | } 159 | 160 | function url($url, $lang=false, $metadata=false) { 161 | 162 | $file = false; 163 | 164 | if(preg_match('!(http|https)\:\/\/!i', $url)) { 165 | return (!$metadata) ? $url : array( 166 | 'url' => $url, 167 | 'file' => $file 168 | ); 169 | } 170 | 171 | if($files = $this->relatedFiles()) { 172 | $file = $files->find($url); 173 | $url = ($file) ? $file->url() : url($url, $lang); 174 | } 175 | 176 | return (!$metadata) ? $url : array( 177 | 'url' => $url, 178 | 'file' => $file 179 | ); 180 | 181 | } 182 | 183 | // get the current related page object 184 | function relatedPage() { 185 | global $site; 186 | return ($this->obj) ? $this->obj : $site->pages()->active(); 187 | } 188 | 189 | // get related files for the related page 190 | function relatedFiles() { 191 | $object = $this->relatedPage(); 192 | return ($object) ? $object->files() : null; 193 | } 194 | 195 | function link($params) { 196 | 197 | $url = @$params['link']; 198 | 199 | // sanitize the url 200 | if(empty($url)) $url = '/'; 201 | 202 | // language attribute is only allowed when lang support is activated 203 | $lang = (!empty($params['lang']) && c::get('lang.support')) ? $params['lang'] : false; 204 | 205 | // get the full href 206 | $href = $this->url($url, $lang); 207 | 208 | $linkAttributes = $this->attr(array( 209 | 'href' => $href, 210 | 'rel' => @$params['rel'], 211 | 'class' => @$params['class'], 212 | 'title' => html(@$params['title']), 213 | )); 214 | 215 | // get the text 216 | $text = (empty($params['text'])) ? $href : $params['text']; 217 | 218 | return '' . html($text) . ''; 219 | 220 | } 221 | 222 | function image($params) { 223 | 224 | $url = @$params['image']; 225 | $alt = @$params['alt']; 226 | $title = @$params['title']; 227 | 228 | // alt is just an alternative for text 229 | if(!empty($params['text'])) $alt = $params['text']; 230 | 231 | // get metadata (url + file) for the image url 232 | $imageMeta = $this->url($url, $lang = false, $metadata = true); 233 | 234 | // try to get the title from the image object and use it as alt text 235 | if($imageMeta['file']) { 236 | 237 | if(empty($alt) && $imageMeta['file']->alt() != '') { 238 | $alt = $imageMeta['file']->alt(); 239 | } 240 | 241 | if(empty($title) && $imageMeta['file']->title() != '') { 242 | $title = $imageMeta['file']->title(); 243 | } 244 | 245 | // last resort for no alt text 246 | if(empty($alt)) $alt = $title; 247 | 248 | } 249 | 250 | $imageAttributes = $this->attr(array( 251 | 'src' => $imageMeta['url'], 252 | 'width' => @$params['width'], 253 | 'height' => @$params['height'], 254 | 'class' => @$params['class'], 255 | 'title' => html($title), 256 | 'alt' => html($alt) 257 | )); 258 | 259 | $image = ''; 260 | 261 | if(!empty($params['link'])) { 262 | 263 | // build the href for the link 264 | $href = ($params['link'] == 'self') ? $url : $params['link']; 265 | 266 | $linkAttributes = $this->attr(array( 267 | 'href' => $this->url($href), 268 | 'rel' => @$params['rel'], 269 | 'class' => @$params['class'], 270 | 'title' => html(@$params['title']), 271 | )); 272 | 273 | return '' . $image . ''; 274 | 275 | } 276 | 277 | return $image; 278 | 279 | } 280 | 281 | function file($params) { 282 | 283 | $url = @$params['file']; 284 | $text = @$params['text']; 285 | $class = @$params['class']; 286 | $rel = @$params['rel']; 287 | $title = @$params['title']; 288 | $download = @$params['download']; 289 | $target = self::target($params); 290 | 291 | if(empty($text)) $text = str_replace('_', '\_', $url); // ignore markdown italic underscores in filenames 292 | if(!empty($class)) $class = ' class="' . $class . '"'; 293 | if(!empty($rel)) $rel = ' rel="' . $rel . '"'; 294 | if(!empty($download)) $download = ' download="' . html($download) . '"'; 295 | if(!empty($title)) $title = ' title="' . html($title) . '"'; 296 | 297 | return '' . html($text) . ''; 298 | 299 | } 300 | 301 | static function attr($name, $value = null) { 302 | if(is_array($name)) { 303 | $attributes = array(); 304 | foreach($name as $key => $val) { 305 | $a = self::attr($key, $val); 306 | if($a) $attributes[] = $a; 307 | } 308 | return implode(' ', $attributes); 309 | } 310 | 311 | if(empty($value)) return false; 312 | return $name . '="' . $value . '"'; 313 | } 314 | 315 | static function date($params) { 316 | $format = @$params['date']; 317 | return (str::lower($format) == 'year') ? date('Y') : date($format); 318 | } 319 | 320 | static function target($params) { 321 | if(empty($params['popup']) && empty($params['target'])) return false; 322 | if(empty($params['popup'])) { 323 | return ' target="' . $params['target'] . '"'; 324 | } else { 325 | return ' target="_blank"'; 326 | } 327 | } 328 | 329 | static function email($params) { 330 | 331 | $url = @$params['email']; 332 | $class = @$params['class']; 333 | $title = @$params['title']; 334 | 335 | if(empty($url)) return false; 336 | return str::email($url, @$params['text'], $title, $class); 337 | 338 | } 339 | 340 | static function twitter($params) { 341 | 342 | $username = @$params['twitter']; 343 | $class = @$params['class']; 344 | $title = @$params['title']; 345 | $target = self::target($params); 346 | 347 | if(empty($username)) return false; 348 | 349 | $username = str_replace('@', '', $username); 350 | $url = 'http://twitter.com/' . $username; 351 | 352 | // add a css class if available 353 | if(!empty($class)) $class = ' class="' . $class . '"'; 354 | if(!empty($title)) $title = ' title="' . html($title) . '"'; 355 | 356 | if(empty($params['text'])) return '@' . html($username) . ''; 357 | 358 | return '' . html($params['text']) . ''; 359 | 360 | } 361 | 362 | static function youtube($params) { 363 | 364 | $url = @$params['youtube']; 365 | $class = @$params['class']; 366 | $id = false; 367 | 368 | // http://www.youtube.com/embed/d9NF2edxy-M 369 | if(@preg_match('!youtube.com\/embed\/([a-z0-9_-]+)!i', $url, $array)) { 370 | $id = @$array[1]; 371 | // http://www.youtube.com/watch?feature=player_embedded&v=d9NF2edxy-M#! 372 | } elseif(@preg_match('!v=([a-z0-9_-]+)!i', $url, $array)) { 373 | $id = @$array[1]; 374 | // http://youtu.be/d9NF2edxy-M 375 | } elseif(@preg_match('!youtu.be\/([a-z0-9_-]+)!i', $url, $array)) { 376 | $id = @$array[1]; 377 | } 378 | 379 | // no id no result! 380 | if(empty($id)) return false; 381 | 382 | // build the embed url for the iframe 383 | $url = 'https://www.youtube.com/embed/' . $id; 384 | 385 | // default width and height if no custom values are set 386 | if(empty($params['width'])) $params['width'] = c::get('kirbytext.video.width'); 387 | if(empty($params['height'])) $params['height'] = c::get('kirbytext.video.height'); 388 | 389 | // add a classname to the iframe 390 | if(!empty($class)) $class = ' class="' . $class . '"'; 391 | 392 | return '
'; 393 | 394 | } 395 | 396 | static function vimeo($params) { 397 | 398 | $url = @$params['vimeo']; 399 | $class = @$params['class']; 400 | 401 | // get the uid from the url 402 | @preg_match('!vimeo.com\/([a-z0-9_-]+)!i', $url, $array); 403 | $id = a::get($array, 1); 404 | 405 | // no id no result! 406 | if(empty($id)) return false; 407 | 408 | // build the embed url for the iframe 409 | $url = 'https://player.vimeo.com/video/' . $id; 410 | 411 | // default width and height if no custom values are set 412 | if(empty($params['width'])) $params['width'] = c::get('kirbytext.video.width'); 413 | if(empty($params['height'])) $params['height'] = c::get('kirbytext.video.height'); 414 | 415 | // add a classname to the iframe 416 | if(!empty($class)) $class = ' class="' . $class . '"'; 417 | 418 | return '
'; 419 | 420 | } 421 | 422 | static function flash($url, $w, $h) { 423 | 424 | if(!$w) $w = c::get('kirbytext.video.width'); 425 | if(!$h) $h = c::get('kirbytext.video.height'); 426 | 427 | return '
'; 428 | 429 | } 430 | 431 | static function gist($params) { 432 | $url = @$params['gist'] . '.js'; 433 | $file = @$params['file']; 434 | if(!empty($file)) { 435 | $url = $url .= '?file=' . $file; 436 | } 437 | return ''; 438 | } 439 | 440 | static function classname() { 441 | return class_exists('kirbytextExtended') ? 'kirbytextExtended' : 'kirbytext'; 442 | } 443 | 444 | function addTags() { 445 | $args = func_get_args(); 446 | $this->tags = array_merge($this->tags, $args); 447 | } 448 | 449 | function addAttributes($attr) { 450 | $args = func_get_args(); 451 | $this->attr = array_merge($this->attr, $args); 452 | } 453 | 454 | } 455 | 456 | -------------------------------------------------------------------------------- /kirby/parsers/yaml.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Chris Wanstrath 15 | * @link http://code.google.com/p/spyc/ 16 | * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen 17 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 18 | * @package Spyc 19 | */ 20 | 21 | if (!function_exists('spyc_load')) { 22 | /** 23 | * Parses YAML to array. 24 | * @param string $string YAML string. 25 | * @return array 26 | */ 27 | function spyc_load ($string) { 28 | return Spyc::YAMLLoadString($string); 29 | } 30 | } 31 | 32 | if (!function_exists('spyc_load_file')) { 33 | /** 34 | * Parses YAML to array. 35 | * @param string $file Path to YAML file. 36 | * @return array 37 | */ 38 | function spyc_load_file ($file) { 39 | return Spyc::YAMLLoad($file); 40 | } 41 | } 42 | 43 | /** 44 | * The Simple PHP YAML Class. 45 | * 46 | * This class can be used to read a YAML file and convert its contents 47 | * into a PHP array. It currently supports a very limited subsection of 48 | * the YAML spec. 49 | * 50 | * Usage: 51 | * 52 | * $Spyc = new Spyc; 53 | * $array = $Spyc->load($file); 54 | * 55 | * or: 56 | * 57 | * $array = Spyc::YAMLLoad($file); 58 | * 59 | * or: 60 | * 61 | * $array = spyc_load_file($file); 62 | * 63 | * @package Spyc 64 | */ 65 | class Spyc { 66 | 67 | // SETTINGS 68 | 69 | const REMPTY = "\0\0\0\0\0"; 70 | 71 | /** 72 | * Setting this to true will force YAMLDump to enclose any string value in 73 | * quotes. False by default. 74 | * 75 | * @var bool 76 | */ 77 | public $setting_dump_force_quotes = false; 78 | 79 | /** 80 | * Setting this to true will forse YAMLLoad to use syck_load function when 81 | * possible. False by default. 82 | * @var bool 83 | */ 84 | public $setting_use_syck_is_possible = false; 85 | 86 | 87 | 88 | /**#@+ 89 | * @access private 90 | * @var mixed 91 | */ 92 | private $_dumpIndent; 93 | private $_dumpWordWrap; 94 | private $_containsGroupAnchor = false; 95 | private $_containsGroupAlias = false; 96 | private $path; 97 | private $result; 98 | private $LiteralPlaceHolder = '___YAML_Literal_Block___'; 99 | private $SavedGroups = array(); 100 | private $indent; 101 | /** 102 | * Path modifier that should be applied after adding current element. 103 | * @var array 104 | */ 105 | private $delayedPath = array(); 106 | 107 | /**#@+ 108 | * @access public 109 | * @var mixed 110 | */ 111 | public $_nodeId; 112 | 113 | /** 114 | * Load a valid YAML string to Spyc. 115 | * @param string $input 116 | * @return array 117 | */ 118 | public function load ($input) { 119 | return $this->__loadString($input); 120 | } 121 | 122 | /** 123 | * Load a valid YAML file to Spyc. 124 | * @param string $file 125 | * @return array 126 | */ 127 | public function loadFile ($file) { 128 | return $this->__load($file); 129 | } 130 | 131 | /** 132 | * Load YAML into a PHP array statically 133 | * 134 | * The load method, when supplied with a YAML stream (string or file), 135 | * will do its best to convert YAML in a file into a PHP array. Pretty 136 | * simple. 137 | * Usage: 138 | * 139 | * $array = Spyc::YAMLLoad('lucky.yaml'); 140 | * print_r($array); 141 | * 142 | * @access public 143 | * @return array 144 | * @param string $input Path of YAML file or string containing YAML 145 | */ 146 | public static function YAMLLoad($input) { 147 | $Spyc = new Spyc; 148 | return $Spyc->__load($input); 149 | } 150 | 151 | /** 152 | * Load a string of YAML into a PHP array statically 153 | * 154 | * The load method, when supplied with a YAML string, will do its best 155 | * to convert YAML in a string into a PHP array. Pretty simple. 156 | * 157 | * Note: use this function if you don't want files from the file system 158 | * loaded and processed as YAML. This is of interest to people concerned 159 | * about security whose input is from a string. 160 | * 161 | * Usage: 162 | * 163 | * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); 164 | * print_r($array); 165 | * 166 | * @access public 167 | * @return array 168 | * @param string $input String containing YAML 169 | */ 170 | public static function YAMLLoadString($input) { 171 | $Spyc = new Spyc; 172 | return $Spyc->__loadString($input); 173 | } 174 | 175 | /** 176 | * Dump YAML from PHP array statically 177 | * 178 | * The dump method, when supplied with an array, will do its best 179 | * to convert the array into friendly YAML. Pretty simple. Feel free to 180 | * save the returned string as nothing.yaml and pass it around. 181 | * 182 | * Oh, and you can decide how big the indent is and what the wordwrap 183 | * for folding is. Pretty cool -- just pass in 'false' for either if 184 | * you want to use the default. 185 | * 186 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 187 | * you can turn off wordwrap by passing in 0. 188 | * 189 | * @access public 190 | * @return string 191 | * @param array $array PHP array 192 | * @param int $indent Pass in false to use the default, which is 2 193 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 194 | */ 195 | public static function YAMLDump($array,$indent = false,$wordwrap = false) { 196 | $spyc = new Spyc; 197 | return $spyc->dump($array,$indent,$wordwrap); 198 | } 199 | 200 | 201 | /** 202 | * Dump PHP array to YAML 203 | * 204 | * The dump method, when supplied with an array, will do its best 205 | * to convert the array into friendly YAML. Pretty simple. Feel free to 206 | * save the returned string as tasteful.yaml and pass it around. 207 | * 208 | * Oh, and you can decide how big the indent is and what the wordwrap 209 | * for folding is. Pretty cool -- just pass in 'false' for either if 210 | * you want to use the default. 211 | * 212 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 213 | * you can turn off wordwrap by passing in 0. 214 | * 215 | * @access public 216 | * @return string 217 | * @param array $array PHP array 218 | * @param int $indent Pass in false to use the default, which is 2 219 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 220 | */ 221 | public function dump($array,$indent = false,$wordwrap = false) { 222 | // Dumps to some very clean YAML. We'll have to add some more features 223 | // and options soon. And better support for folding. 224 | 225 | // New features and options. 226 | if ($indent === false or !is_numeric($indent)) { 227 | $this->_dumpIndent = 2; 228 | } else { 229 | $this->_dumpIndent = $indent; 230 | } 231 | 232 | if ($wordwrap === false or !is_numeric($wordwrap)) { 233 | $this->_dumpWordWrap = 40; 234 | } else { 235 | $this->_dumpWordWrap = $wordwrap; 236 | } 237 | 238 | // New YAML document 239 | $string = "---\n"; 240 | 241 | // Start at the base of the array and move through it. 242 | if ($array) { 243 | $array = (array)$array; 244 | $previous_key = -1; 245 | foreach ($array as $key => $value) { 246 | if (!isset($first_key)) $first_key = $key; 247 | $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array); 248 | $previous_key = $key; 249 | } 250 | } 251 | return $string; 252 | } 253 | 254 | /** 255 | * Attempts to convert a key / value array item to YAML 256 | * @access private 257 | * @return string 258 | * @param $key The name of the key 259 | * @param $value The value of the item 260 | * @param $indent The indent of the current node 261 | */ 262 | private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) { 263 | if (is_array($value)) { 264 | if (empty ($value)) 265 | return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array); 266 | // It has children. What to do? 267 | // Make it the right kind of item 268 | $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array); 269 | // Add the indent 270 | $indent += $this->_dumpIndent; 271 | // Yamlize the array 272 | $string .= $this->_yamlizeArray($value,$indent); 273 | } elseif (!is_array($value)) { 274 | // It doesn't have children. Yip. 275 | $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array); 276 | } 277 | return $string; 278 | } 279 | 280 | /** 281 | * Attempts to convert an array to YAML 282 | * @access private 283 | * @return string 284 | * @param $array The array you want to convert 285 | * @param $indent The indent of the current level 286 | */ 287 | private function _yamlizeArray($array,$indent) { 288 | if (is_array($array)) { 289 | $string = ''; 290 | $previous_key = -1; 291 | foreach ($array as $key => $value) { 292 | if (!isset($first_key)) $first_key = $key; 293 | $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array); 294 | $previous_key = $key; 295 | } 296 | return $string; 297 | } else { 298 | return false; 299 | } 300 | } 301 | 302 | /** 303 | * Returns YAML from a key and a value 304 | * @access private 305 | * @return string 306 | * @param $key The name of the key 307 | * @param $value The value of the item 308 | * @param $indent The indent of the current node 309 | */ 310 | private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) { 311 | // do some folding here, for blocks 312 | if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || 313 | strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, ' ') !== false || 314 | strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 || 315 | substr ($value, -1, 1) == ':') 316 | ) { 317 | $value = $this->_doLiteralBlock($value,$indent); 318 | } else { 319 | $value = $this->_doFolding($value,$indent); 320 | } 321 | 322 | if ($value === array()) $value = '[ ]'; 323 | if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) { 324 | $value = $this->_doLiteralBlock($value,$indent); 325 | } 326 | if (trim ($value) != $value) 327 | $value = $this->_doLiteralBlock($value,$indent); 328 | 329 | if (is_bool($value)) { 330 | $value = ($value) ? "true" : "false"; 331 | } 332 | 333 | if ($value === null) $value = 'null'; 334 | if ($value === "'" . self::REMPTY . "'") $value = null; 335 | 336 | $spaces = str_repeat(' ',$indent); 337 | 338 | //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { 339 | if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) { 340 | // It's a sequence 341 | $string = $spaces.'- '.$value."\n"; 342 | } else { 343 | // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); 344 | // It's mapped 345 | if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; } 346 | $string = rtrim ($spaces.$key.': '.$value)."\n"; 347 | } 348 | return $string; 349 | } 350 | 351 | /** 352 | * Creates a literal block for dumping 353 | * @access private 354 | * @return string 355 | * @param $value 356 | * @param $indent int The value of the indent 357 | */ 358 | private function _doLiteralBlock($value,$indent) { 359 | if ($value === "\n") return '\n'; 360 | if (strpos($value, "\n") === false && strpos($value, "'") === false) { 361 | return sprintf ("'%s'", $value); 362 | } 363 | if (strpos($value, "\n") === false && strpos($value, '"') === false) { 364 | return sprintf ('"%s"', $value); 365 | } 366 | $exploded = explode("\n",$value); 367 | $newValue = '|'; 368 | $indent += $this->_dumpIndent; 369 | $spaces = str_repeat(' ',$indent); 370 | foreach ($exploded as $line) { 371 | $newValue .= "\n" . $spaces . ($line); 372 | } 373 | return $newValue; 374 | } 375 | 376 | /** 377 | * Folds a string of text, if necessary 378 | * @access private 379 | * @return string 380 | * @param $value The string you wish to fold 381 | */ 382 | private function _doFolding($value,$indent) { 383 | // Don't do anything if wordwrap is set to 0 384 | 385 | if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { 386 | $indent += $this->_dumpIndent; 387 | $indent = str_repeat(' ',$indent); 388 | $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); 389 | $value = ">\n".$indent.$wrapped; 390 | } else { 391 | if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY) 392 | $value = '"' . $value . '"'; 393 | } 394 | 395 | 396 | return $value; 397 | } 398 | 399 | // LOADING FUNCTIONS 400 | 401 | private function __load($input) { 402 | $Source = $this->loadFromSource($input); 403 | return $this->loadWithSource($Source); 404 | } 405 | 406 | private function __loadString($input) { 407 | $Source = $this->loadFromString($input); 408 | return $this->loadWithSource($Source); 409 | } 410 | 411 | private function loadWithSource($Source) { 412 | if (empty ($Source)) return array(); 413 | if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { 414 | $array = syck_load (implode ('', $Source)); 415 | return is_array($array) ? $array : array(); 416 | } 417 | 418 | $this->path = array(); 419 | $this->result = array(); 420 | 421 | $cnt = count($Source); 422 | for ($i = 0; $i < $cnt; $i++) { 423 | $line = $Source[$i]; 424 | 425 | $this->indent = strlen($line) - strlen(ltrim($line)); 426 | $tempPath = $this->getParentPathByIndent($this->indent); 427 | $line = self::stripIndent($line, $this->indent); 428 | if (self::isComment($line)) continue; 429 | if (self::isEmpty($line)) continue; 430 | $this->path = $tempPath; 431 | 432 | $literalBlockStyle = self::startsLiteralBlock($line); 433 | if ($literalBlockStyle) { 434 | $line = rtrim ($line, $literalBlockStyle . " \n"); 435 | $literalBlock = ''; 436 | $line .= $this->LiteralPlaceHolder; 437 | $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1])); 438 | while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { 439 | $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent); 440 | } 441 | $i--; 442 | } 443 | 444 | while (++$i < $cnt && self::greedilyNeedNextLine($line)) { 445 | $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); 446 | } 447 | $i--; 448 | 449 | 450 | 451 | if (strpos ($line, '#')) { 452 | if (strpos ($line, '"') === false && strpos ($line, "'") === false) 453 | $line = preg_replace('/\s+#(.+)$/','',$line); 454 | } 455 | 456 | $lineArray = $this->_parseLine($line); 457 | 458 | if ($literalBlockStyle) 459 | $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); 460 | 461 | $this->addArray($lineArray, $this->indent); 462 | 463 | foreach ($this->delayedPath as $indent => $delayedPath) 464 | $this->path[$indent] = $delayedPath; 465 | 466 | $this->delayedPath = array(); 467 | 468 | } 469 | return $this->result; 470 | } 471 | 472 | private function loadFromSource ($input) { 473 | if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) 474 | return file($input); 475 | 476 | return $this->loadFromString($input); 477 | } 478 | 479 | private function loadFromString ($input) { 480 | $lines = explode("\n",$input); 481 | foreach ($lines as $k => $_) { 482 | $lines[$k] = rtrim ($_, "\r"); 483 | } 484 | return $lines; 485 | } 486 | 487 | /** 488 | * Parses YAML code and returns an array for a node 489 | * @access private 490 | * @return array 491 | * @param string $line A line from the YAML file 492 | */ 493 | private function _parseLine($line) { 494 | if (!$line) return array(); 495 | $line = trim($line); 496 | if (!$line) return array(); 497 | 498 | $array = array(); 499 | 500 | $group = $this->nodeContainsGroup($line); 501 | if ($group) { 502 | $this->addGroup($line, $group); 503 | $line = $this->stripGroup ($line, $group); 504 | } 505 | 506 | if ($this->startsMappedSequence($line)) 507 | return $this->returnMappedSequence($line); 508 | 509 | if ($this->startsMappedValue($line)) 510 | return $this->returnMappedValue($line); 511 | 512 | if ($this->isArrayElement($line)) 513 | return $this->returnArrayElement($line); 514 | 515 | if ($this->isPlainArray($line)) 516 | return $this->returnPlainArray($line); 517 | 518 | 519 | return $this->returnKeyValuePair($line); 520 | 521 | } 522 | 523 | /** 524 | * Finds the type of the passed value, returns the value as the new type. 525 | * @access private 526 | * @param string $value 527 | * @return mixed 528 | */ 529 | private function _toType($value) { 530 | if ($value === '') return null; 531 | $first_character = $value[0]; 532 | $last_character = substr($value, -1, 1); 533 | 534 | $is_quoted = false; 535 | do { 536 | if (!$value) break; 537 | if ($first_character != '"' && $first_character != "'") break; 538 | if ($last_character != '"' && $last_character != "'") break; 539 | $is_quoted = true; 540 | } while (0); 541 | 542 | if ($is_quoted) 543 | return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); 544 | 545 | if (strpos($value, ' #') !== false && !$is_quoted) 546 | $value = preg_replace('/\s+#(.+)$/','',$value); 547 | 548 | if (!$is_quoted) $value = str_replace('\n', "\n", $value); 549 | 550 | if ($first_character == '[' && $last_character == ']') { 551 | // Take out strings sequences and mappings 552 | $innerValue = trim(substr ($value, 1, -1)); 553 | if ($innerValue === '') return array(); 554 | $explode = $this->_inlineEscape($innerValue); 555 | // Propagate value array 556 | $value = array(); 557 | foreach ($explode as $v) { 558 | $value[] = $this->_toType($v); 559 | } 560 | return $value; 561 | } 562 | 563 | if (strpos($value,': ')!==false && $first_character != '{') { 564 | $array = explode(': ',$value); 565 | $key = trim($array[0]); 566 | array_shift($array); 567 | $value = trim(implode(': ',$array)); 568 | $value = $this->_toType($value); 569 | return array($key => $value); 570 | } 571 | 572 | if ($first_character == '{' && $last_character == '}') { 573 | $innerValue = trim(substr ($value, 1, -1)); 574 | if ($innerValue === '') return array(); 575 | // Inline Mapping 576 | // Take out strings sequences and mappings 577 | $explode = $this->_inlineEscape($innerValue); 578 | // Propagate value array 579 | $array = array(); 580 | foreach ($explode as $v) { 581 | $SubArr = $this->_toType($v); 582 | if (empty($SubArr)) continue; 583 | if (is_array ($SubArr)) { 584 | $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; 585 | } 586 | $array[] = $SubArr; 587 | } 588 | return $array; 589 | } 590 | 591 | if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { 592 | return null; 593 | } 594 | 595 | if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){ 596 | $intvalue = (int)$value; 597 | if ($intvalue != PHP_INT_MAX) 598 | $value = $intvalue; 599 | return $value; 600 | } 601 | 602 | if (in_array($value, 603 | array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { 604 | return true; 605 | } 606 | 607 | if (in_array(strtolower($value), 608 | array('false', 'off', '-', 'no', 'n'))) { 609 | return false; 610 | } 611 | 612 | if (is_numeric($value)) { 613 | if ($value === '0') return 0; 614 | if (rtrim ($value, 0) === $value) 615 | $value = (float)$value; 616 | return $value; 617 | } 618 | 619 | return $value; 620 | } 621 | 622 | /** 623 | * Used in inlines to check for more inlines or quoted strings 624 | * @access private 625 | * @return array 626 | */ 627 | private function _inlineEscape($inline) { 628 | // There's gotta be a cleaner way to do this... 629 | // While pure sequences seem to be nesting just fine, 630 | // pure mappings and mappings with sequences inside can't go very 631 | // deep. This needs to be fixed. 632 | 633 | $seqs = array(); 634 | $maps = array(); 635 | $saved_strings = array(); 636 | 637 | // Check for strings 638 | $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; 639 | if (preg_match_all($regex,$inline,$strings)) { 640 | $saved_strings = $strings[0]; 641 | $inline = preg_replace($regex,'YAMLString',$inline); 642 | } 643 | unset($regex); 644 | 645 | $i = 0; 646 | do { 647 | 648 | // Check for sequences 649 | while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { 650 | $seqs[] = $matchseqs[0]; 651 | $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); 652 | } 653 | 654 | // Check for mappings 655 | while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { 656 | $maps[] = $matchmaps[0]; 657 | $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); 658 | } 659 | 660 | if ($i++ >= 10) break; 661 | 662 | } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); 663 | 664 | $explode = explode(', ',$inline); 665 | $stringi = 0; $i = 0; 666 | 667 | while (1) { 668 | 669 | // Re-add the sequences 670 | if (!empty($seqs)) { 671 | foreach ($explode as $key => $value) { 672 | if (strpos($value,'YAMLSeq') !== false) { 673 | foreach ($seqs as $seqk => $seq) { 674 | $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); 675 | $value = $explode[$key]; 676 | } 677 | } 678 | } 679 | } 680 | 681 | // Re-add the mappings 682 | if (!empty($maps)) { 683 | foreach ($explode as $key => $value) { 684 | if (strpos($value,'YAMLMap') !== false) { 685 | foreach ($maps as $mapk => $map) { 686 | $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); 687 | $value = $explode[$key]; 688 | } 689 | } 690 | } 691 | } 692 | 693 | 694 | // Re-add the strings 695 | if (!empty($saved_strings)) { 696 | foreach ($explode as $key => $value) { 697 | while (strpos($value,'YAMLString') !== false) { 698 | $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); 699 | unset($saved_strings[$stringi]); 700 | ++$stringi; 701 | $value = $explode[$key]; 702 | } 703 | } 704 | } 705 | 706 | $finished = true; 707 | foreach ($explode as $key => $value) { 708 | if (strpos($value,'YAMLSeq') !== false) { 709 | $finished = false; break; 710 | } 711 | if (strpos($value,'YAMLMap') !== false) { 712 | $finished = false; break; 713 | } 714 | if (strpos($value,'YAMLString') !== false) { 715 | $finished = false; break; 716 | } 717 | } 718 | if ($finished) break; 719 | 720 | $i++; 721 | if ($i > 10) 722 | break; // Prevent infinite loops. 723 | } 724 | 725 | return $explode; 726 | } 727 | 728 | private function literalBlockContinues ($line, $lineIndent) { 729 | if (!trim($line)) return true; 730 | if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; 731 | return false; 732 | } 733 | 734 | private function referenceContentsByAlias ($alias) { 735 | do { 736 | if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } 737 | $groupPath = $this->SavedGroups[$alias]; 738 | $value = $this->result; 739 | foreach ($groupPath as $k) { 740 | $value = $value[$k]; 741 | } 742 | } while (false); 743 | return $value; 744 | } 745 | 746 | private function addArrayInline ($array, $indent) { 747 | $CommonGroupPath = $this->path; 748 | if (empty ($array)) return false; 749 | 750 | foreach ($array as $k => $_) { 751 | $this->addArray(array($k => $_), $indent); 752 | $this->path = $CommonGroupPath; 753 | } 754 | return true; 755 | } 756 | 757 | private function addArray ($incoming_data, $incoming_indent) { 758 | 759 | // print_r ($incoming_data); 760 | 761 | if (count ($incoming_data) > 1) 762 | return $this->addArrayInline ($incoming_data, $incoming_indent); 763 | 764 | $key = key ($incoming_data); 765 | $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; 766 | if ($key === '__!YAMLZero') $key = '0'; 767 | 768 | if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. 769 | if ($key || $key === '' || $key === '0') { 770 | $this->result[$key] = $value; 771 | } else { 772 | $this->result[] = $value; end ($this->result); $key = key ($this->result); 773 | } 774 | $this->path[$incoming_indent] = $key; 775 | return; 776 | } 777 | 778 | 779 | 780 | $history = array(); 781 | // Unfolding inner array tree. 782 | $history[] = $_arr = $this->result; 783 | foreach ($this->path as $k) { 784 | $history[] = $_arr = $_arr[$k]; 785 | } 786 | 787 | if ($this->_containsGroupAlias) { 788 | $value = $this->referenceContentsByAlias($this->_containsGroupAlias); 789 | $this->_containsGroupAlias = false; 790 | } 791 | 792 | 793 | // Adding string or numeric key to the innermost level or $this->arr. 794 | if (is_string($key) && $key == '<<') { 795 | if (!is_array ($_arr)) { $_arr = array (); } 796 | 797 | $_arr = array_merge ($_arr, $value); 798 | } else if ($key || $key === '' || $key === '0') { 799 | if (!is_array ($_arr)) 800 | $_arr = array ($key=>$value); 801 | else 802 | $_arr[$key] = $value; 803 | } else { 804 | if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } 805 | else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } 806 | } 807 | 808 | $reverse_path = array_reverse($this->path); 809 | $reverse_history = array_reverse ($history); 810 | $reverse_history[0] = $_arr; 811 | $cnt = count($reverse_history) - 1; 812 | for ($i = 0; $i < $cnt; $i++) { 813 | $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; 814 | } 815 | $this->result = $reverse_history[$cnt]; 816 | 817 | $this->path[$incoming_indent] = $key; 818 | 819 | if ($this->_containsGroupAnchor) { 820 | $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; 821 | if (is_array ($value)) { 822 | $k = key ($value); 823 | if (!is_int ($k)) { 824 | $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; 825 | } 826 | } 827 | $this->_containsGroupAnchor = false; 828 | } 829 | 830 | } 831 | 832 | private static function startsLiteralBlock ($line) { 833 | $lastChar = substr (trim($line), -1); 834 | if ($lastChar != '>' && $lastChar != '|') return false; 835 | if ($lastChar == '|') return $lastChar; 836 | // HTML tags should not be counted as literal blocks. 837 | if (preg_match ('#<.*?>$#', $line)) return false; 838 | return $lastChar; 839 | } 840 | 841 | private static function greedilyNeedNextLine($line) { 842 | $line = trim ($line); 843 | if (!strlen($line)) return false; 844 | if (substr ($line, -1, 1) == ']') return false; 845 | if ($line[0] == '[') return true; 846 | if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; 847 | return false; 848 | } 849 | 850 | private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) { 851 | $line = self::stripIndent($line, $indent); 852 | if ($literalBlockStyle !== '|') { 853 | $line = self::stripIndent($line); 854 | } 855 | $line = rtrim ($line, "\r\n\t ") . "\n"; 856 | if ($literalBlockStyle == '|') { 857 | return $literalBlock . $line; 858 | } 859 | if (strlen($line) == 0) 860 | return rtrim($literalBlock, ' ') . "\n"; 861 | if ($line == "\n" && $literalBlockStyle == '>') { 862 | return rtrim ($literalBlock, " \t") . "\n"; 863 | } 864 | if ($line != "\n") 865 | $line = trim ($line, "\r\n ") . " "; 866 | return $literalBlock . $line; 867 | } 868 | 869 | function revertLiteralPlaceHolder ($lineArray, $literalBlock) { 870 | foreach ($lineArray as $k => $_) { 871 | if (is_array($_)) 872 | $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); 873 | else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) 874 | $lineArray[$k] = rtrim ($literalBlock, " \r\n"); 875 | } 876 | return $lineArray; 877 | } 878 | 879 | private static function stripIndent ($line, $indent = -1) { 880 | if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); 881 | return substr ($line, $indent); 882 | } 883 | 884 | private function getParentPathByIndent ($indent) { 885 | if ($indent == 0) return array(); 886 | $linePath = $this->path; 887 | do { 888 | end($linePath); $lastIndentInParentPath = key($linePath); 889 | if ($indent <= $lastIndentInParentPath) array_pop ($linePath); 890 | } while ($indent <= $lastIndentInParentPath); 891 | return $linePath; 892 | } 893 | 894 | 895 | private function clearBiggerPathValues ($indent) { 896 | 897 | 898 | if ($indent == 0) $this->path = array(); 899 | if (empty ($this->path)) return true; 900 | 901 | foreach ($this->path as $k => $_) { 902 | if ($k > $indent) unset ($this->path[$k]); 903 | } 904 | 905 | return true; 906 | } 907 | 908 | 909 | private static function isComment ($line) { 910 | if (!$line) return false; 911 | if ($line[0] == '#') return true; 912 | if (trim($line, " \r\n\t") == '---') return true; 913 | return false; 914 | } 915 | 916 | private static function isEmpty ($line) { 917 | return (trim ($line) === ''); 918 | } 919 | 920 | 921 | private function isArrayElement ($line) { 922 | if (!$line) return false; 923 | if ($line[0] != '-') return false; 924 | if (strlen ($line) > 3) 925 | if (substr($line,0,3) == '---') return false; 926 | 927 | return true; 928 | } 929 | 930 | private function isHashElement ($line) { 931 | return strpos($line, ':'); 932 | } 933 | 934 | private function isLiteral ($line) { 935 | if ($this->isArrayElement($line)) return false; 936 | if ($this->isHashElement($line)) return false; 937 | return true; 938 | } 939 | 940 | 941 | private static function unquote ($value) { 942 | if (!$value) return $value; 943 | if (!is_string($value)) return $value; 944 | if ($value[0] == '\'') return trim ($value, '\''); 945 | if ($value[0] == '"') return trim ($value, '"'); 946 | return $value; 947 | } 948 | 949 | private function startsMappedSequence ($line) { 950 | return ($line[0] == '-' && substr ($line, -1, 1) == ':'); 951 | } 952 | 953 | private function returnMappedSequence ($line) { 954 | $array = array(); 955 | $key = self::unquote(trim(substr($line,1,-1))); 956 | $array[$key] = array(); 957 | $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); 958 | return array($array); 959 | } 960 | 961 | private function returnMappedValue ($line) { 962 | $array = array(); 963 | $key = self::unquote (trim(substr($line,0,-1))); 964 | $array[$key] = ''; 965 | return $array; 966 | } 967 | 968 | private function startsMappedValue ($line) { 969 | return (substr ($line, -1, 1) == ':'); 970 | } 971 | 972 | private function isPlainArray ($line) { 973 | return ($line[0] == '[' && substr ($line, -1, 1) == ']'); 974 | } 975 | 976 | private function returnPlainArray ($line) { 977 | return $this->_toType($line); 978 | } 979 | 980 | private function returnKeyValuePair ($line) { 981 | $array = array(); 982 | $key = ''; 983 | if (strpos ($line, ':')) { 984 | // It's a key/value pair most likely 985 | // If the key is in double quotes pull it out 986 | if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { 987 | $value = trim(str_replace($matches[1],'',$line)); 988 | $key = $matches[2]; 989 | } else { 990 | // Do some guesswork as to the key and the value 991 | $explode = explode(':',$line); 992 | $key = trim($explode[0]); 993 | array_shift($explode); 994 | $value = trim(implode(':',$explode)); 995 | } 996 | // Set the type of the value. Int, string, etc 997 | $value = $this->_toType($value); 998 | if ($key === '0') $key = '__!YAMLZero'; 999 | $array[$key] = $value; 1000 | } else { 1001 | $array = array ($line); 1002 | } 1003 | return $array; 1004 | 1005 | } 1006 | 1007 | 1008 | private function returnArrayElement ($line) { 1009 | if (strlen($line) <= 1) return array(array()); // Weird %) 1010 | $array = array(); 1011 | $value = trim(substr($line,1)); 1012 | $value = $this->_toType($value); 1013 | $array[] = $value; 1014 | return $array; 1015 | } 1016 | 1017 | 1018 | private function nodeContainsGroup ($line) { 1019 | $symbolsForReference = 'A-z0-9_\-'; 1020 | if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) 1021 | if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; 1022 | if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; 1023 | if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; 1024 | if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; 1025 | if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; 1026 | return false; 1027 | 1028 | } 1029 | 1030 | private function addGroup ($line, $group) { 1031 | if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); 1032 | if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); 1033 | //print_r ($this->path); 1034 | } 1035 | 1036 | private function stripGroup ($line, $group) { 1037 | $line = trim(str_replace($group, '', $line)); 1038 | return $line; 1039 | } 1040 | } 1041 | 1042 | // Enable use of Spyc from command line 1043 | // The syntax is the following: php spyc.php spyc.yaml 1044 | 1045 | define ('SPYC_FROM_COMMAND_LINE', false); 1046 | 1047 | do { 1048 | if (!SPYC_FROM_COMMAND_LINE) break; 1049 | if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; 1050 | if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break; 1051 | $file = $argv[1]; 1052 | printf ("Spyc loading file: %s\n", $file); 1053 | print_r (spyc_load_file ($file)); 1054 | } while (0); -------------------------------------------------------------------------------- /kirby/system.php: -------------------------------------------------------------------------------- 1 | load(); 66 | 67 | -------------------------------------------------------------------------------- /license.mdown: -------------------------------------------------------------------------------- 1 | #Kirby End User License Agreement 2 | 3 | 4 | This End User License Agreement (the "Agreement") is a binding legal agreement between you and the Bastian Allgeier GmbH (the "Author"). By installing or using the Kirby CMS (the "Software"), you agree to be bound by the terms of this Agreement. If you do not agree to the Agreement, do not download, install, or use the Software. Installation or use of the Software signifies that you have read, understood, and agreed to be bound by the Agreement. 5 | 6 | Revised on: 13 March, 2014 7 | 8 | ##Usage 9 | 10 | This Agreement grants a non-exclusive, non-transferable license to install and use a single instance of the Software on a specific Website. Additional Software licenses must be purchased in order to install and use the Software on additional Websites. The Author reserves the right to determine whether use of the Software qualifies under this Agreement. The Author owns all rights, title and interest to the Software (including all intellectual property rights) and reserves all rights to the Software that are not expressly granted in this Agreement. 11 | 12 | ##Backups 13 | 14 | You may make copies of the Software in any machine readable form solely for back-up purposes, provided that you reproduce the Software in its original form and with all proprietary notices on the back-up copy. All rights to the Software not expressly granted herein are reserved by the Author. 15 | 16 | ##Technical Support 17 | 18 | Technical support is provided as described on . No representations or guarantees are made regarding the response time in which support questions are answered. 19 | 20 | ##Refund Policy 21 | 22 | We offer a 14-day, money back refund policy. 23 | 24 | ##Restrictions 25 | 26 | You understand and agree that you shall only use the Software in a manner that complies with any and all applicable laws in the jurisdictions in which you use the Software. Your use shall be in accordance with applicable restrictions concerning privacy and intellectual property rights. 27 | 28 | ###You may not: 29 | 30 | Distribute derivative works based on the Software; 31 | Reproduce the Software except as described in this Agreement; 32 | Sell, assign, license, disclose, distribute, or otherwise transfer or make available the Software or its Source Code, in whole or in part, in any form to any third parties; 33 | Use the Software to provide services to others; 34 | Remove or alter any proprietary notices on the Software. 35 | 36 | ##No Warranty 37 | 38 | THE SOFTWARE IS OFFERED ON AN "AS-IS" BASIS AND NO WARRANTY, EITHER EXPRESSED OR IMPLIED, IS GIVEN. THE AUTHOR EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. YOU ASSUME ALL RISK ASSOCIATED WITH THE QUALITY, PERFORMANCE, INSTALLATION AND USE OF THE SOFTWARE INCLUDING, BUT NOT LIMITED TO, THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USE THE SOFTWARE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE. 39 | 40 | ##Term, Termination, and Modification. 41 | 42 | You may use the Software under this Agreement until either party terminates this Agreement as set forth in this paragraph. Either party may terminate the Agreement at any time, upon written notice to the other party. Upon termination, all licenses granted to you will terminate, and you will immediately uninstall and cease all use of the Software. The Sections entitled "No Warranty," "Indemnification," and "Limitation of Liability" will survive any termination of this Agreement. 43 | 44 | The Author may modify the Software and this Agreement with notice to you either in email or by publishing content on the Software website, including but not limited to changing the functionality or appearance of the Software, and such modification will become binding on you unless you terminate this Agreement. 45 | 46 | ##Indemnification. 47 | 48 | By accepting the Agreement, you agree to indemnify and otherwise hold harmless the Author, its officers, employers, agents, subsidiaries, affiliates and other partners from any direct, indirect, incidental, special, consequential or exemplary damages arising out of, relating to, or resulting from your use of the Software or any other matter relating to the Software. 49 | 50 | ##Limitation of Liability. 51 | 52 | YOU EXPRESSLY UNDERSTAND AND AGREE THAT THE AUTHOR SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES, INCLUDING BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER INTANGIBLE LOSSES (EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES). SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU. IN NO EVENT WILL THE AUTHOR'S TOTAL CUMULATIVE DAMAGES EXCEED THE FEES YOU PAID TO THE AUTHOR UNDER THIS AGREEMENT IN THE MOST RECENT TWELVE-MONTH PERIOD. 53 | 54 | #Definitions 55 | 56 | ##Definition of Website 57 | 58 | A "Website" is defined as a single domain including sub-domains that operate as a single entity. What constitutes a single entity shall be at the sole discretion of the Author. 59 | 60 | ##Definition of Source Code 61 | 62 | The "Source Code" is defined as the contents of all HTML, CSS, JavaScript, and PHP files provided with the Software and includes all related image files and database schemas. 63 | 64 | ##Definition of an Update 65 | 66 | An "Update" of the Software is defined as that which adds minor functionality enhancements or any bug fix to the current version. This class of release is identified by the change of the revision to the right of the decimal point, i.e. X.1 to X.2 67 | 68 | The assignment to the category of Update or Upgrade shall be at the sole discretion of the Author. 69 | 70 | ##Definition of an Upgrade 71 | 72 | An "Upgrade" is a major release of the Software and is defined as that which incorporates major new features or enhancement that increase the core functionality of the software. This class of release is identified by the change of the revision to the left of the decimal point, i.e. 4.X to 5.X 73 | 74 | The assignment to the category of Update or Upgrade shall be at the sole discretion of the Author. 75 | -------------------------------------------------------------------------------- /readme.mdown: -------------------------------------------------------------------------------- 1 | # Kirby 1 2 | 3 | **Deprecated. See http://github.com/getkirby** 4 | 5 | -------------------------------------------------------------------------------- /site/cache/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getkirby-v1/starterkit/d547c0abcdf9e100abc55972243b7d83a783d867/site/cache/index.html -------------------------------------------------------------------------------- /site/config/config.php: -------------------------------------------------------------------------------- 1 | 2 | copyright()) ?> 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/snippets/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <?php echo html($site->title()) ?> - <?php echo html($page->title()) ?> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

<?php echo html($site->title()) ?>

19 |
-------------------------------------------------------------------------------- /site/snippets/menu.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site/snippets/submenu.php: -------------------------------------------------------------------------------- 1 | findOpen(); 5 | $items = ($open) ? $open->children()->visible() : false; 6 | 7 | ?> 8 | count()): ?> 9 | 16 | 17 | -------------------------------------------------------------------------------- /site/templates/default.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |

title()) ?>

9 | text()) ?> 10 |
11 | 12 |
13 | 14 | --------------------------------------------------------------------------------