30 |
31 |
--------------------------------------------------------------------------------
/plugins/highlight/styles/objc.css:
--------------------------------------------------------------------------------
1 | .objc .de1, .objc .de2 {margin:0; padding:0; background:none; vertical-align:top;}
2 | .objc .imp {font-weight: bold; color: red;}
3 | .objc li, .objc .li1 {font-weight: normal; vertical-align:top;}
4 | .objc .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
5 | .objc .li2 {font-weight: bold; vertical-align:top;}
6 | .objc .kw1 {color: #a61390;}
7 | .objc .kw2 {color: #a61390;}
8 | .objc .kw3 {color: #a61390;}
9 | .objc .kw4 {color: #a61390;}
10 | .objc .kw5 {color: #400080;}
11 | .objc .kw6 {color: #2a6f76;}
12 | .objc .kw7 {color: #400080;}
13 | .objc .kw8 {color: #2a6f76;}
14 | .objc .kw9 {color: #400080;}
15 | .objc .co1 {color: #6e371a;}
16 | .objc .co2 {color: #11740a; font-style: italic;}
17 | .objc .co3 {color: #bf1d1a;}
18 | .objc .coMULTI {color: #11740a; font-style: italic;}
19 | .objc .es0 {color: #2400d9;}
20 | .objc .br0 {color: #002200;}
21 | .objc .sy0 {color: #002200;}
22 | .objc .st0 {color: #bf1d1a;}
23 | .objc .nu0 {color: #2400d9;}
24 | .objc .ln-xtra, .objc li.ln-xtra, .objc div.ln-xtra {background-color: #ffc;}
25 | .objc span.xtra { display:block; }
26 |
27 |
--------------------------------------------------------------------------------
/plugins/highlight/highlight.php:
--------------------------------------------------------------------------------
1 | set_header_type(GESHI_HEADER_NONE);
13 | $geshi->enable_classes();
14 | $geshi->enable_keyword_links(false);
15 | $code = $geshi->parse_code();
16 | return '' . $code . '';
17 |
18 | }
19 |
20 | function smartHighlight($code) {
21 |
22 | // find all php stuff
23 | preg_match_all('!\<\?.*?\?\>!i', $code, $array);
24 |
25 | $result = '';
26 | $php = array();
27 |
28 | // replace php within html with placeholders
29 | foreach($array[0] as $key => $found) {
30 | $key = '____' . $key . '____';
31 | $php[$key] = highlight($found, 'php');
32 | $code = str_replace($found, $key, $code);
33 | }
34 |
35 | // now highlight the plain html
36 | $code = highlight($code, 'html');
37 |
38 | // put the highlighted php back in
39 | foreach($php as $key => $p) {
40 | $code = str_replace($key, $p, $code);
41 | }
42 |
43 | return $code;
44 |
45 | }
--------------------------------------------------------------------------------
/snippets/video/readme.mdown:
--------------------------------------------------------------------------------
1 | # Video Snippet
2 |
3 | ## What is it?
4 |
5 | a very simple helper to build full html 5 video tags.
6 |
7 | ## Installation
8 |
9 | Put the video.php file in your site/snippets folder
10 |
11 | ## How to use it?
12 |
13 | videos()->find('movie.mp4'),
18 | $page->videos()->find('movie.mobile.mp4'),
19 | $page->videos()->find('movie.webm'),
20 | $page->videos()->find('movie.ogv'),
21 | );
22 |
23 | snippet('video', array(
24 | 'videos' => $videos,
25 | 'thumb' => $page->images()->find('movie.jpg')
26 | ));
27 |
28 | ?>
29 |
30 | ## Options
31 |
32 | ### width
33 |
34 | Set the width of the video. Default is 400px
35 |
36 | ### height
37 |
38 | Set the height of the video. Default is 300px
39 |
40 | ### preload
41 |
42 | (true|false) Default is true
43 |
44 | ### controls
45 |
46 | (true|false) Display controls for the video. Default is true
47 |
48 | ## Customization
49 |
50 | Change the html in site/snippets/video.php to get the best results for your project
51 |
52 | ## Author
53 | Bastian Allgeier
54 |
--------------------------------------------------------------------------------
/snippets/filelist/readme.mdown:
--------------------------------------------------------------------------------
1 | # Filelist Snippet
2 |
3 | ## What is it?
4 |
5 | Filelist will generate a list of all or any matching files in your page's content folder. This is pretty handy for building download lists for example.
6 |
7 | ## Installation
8 |
9 | Put the filelist.php file in your site/snippets folder
10 |
11 | ## How to use it?
12 |
13 |
14 |
15 | ## Options
16 |
17 | 'zip',
21 | 'sort' => 'filename',
22 | 'direction' => 'desc' // can be asc or desc
23 | ));
24 |
25 | ?>
26 |
27 | By passing additional variables you can customize the output of your list:
28 |
29 | ### extension
30 |
31 | Set the extension to only get a list of files with that extension. All files will be found by default if you don't specify an extension
32 |
33 | ### sort
34 |
35 | Change the sort field. Default is "filename"
36 |
37 | ### direction
38 |
39 | Change the sort direction. Default is "desc"
40 |
41 |
42 | ## Customization
43 |
44 | Customize the html in site/snippets/gallery.php to get the best result for your project
45 |
46 | ## Author
47 | Bastian Allgeier
48 |
--------------------------------------------------------------------------------
/exporters/posterous/readme.mdown:
--------------------------------------------------------------------------------
1 | # Posterous Article Exporter
2 |
3 | This exporter will grab all articles from your Posterous blog and export them to folders and text files, which will be readable by Kirby.
4 |
5 | ## Installation
6 |
7 | Download posterous.php and put it in the main directory of your Kirby site. Open posterous.php in your favorite editor and setup the name of your blog, your posterous username and password and the posterous API token. You should also make sure that $root is setup to the folder where all the exported articles should be stored and that this folder is writable. (change permissions to 0755)
8 |
9 | MAKE A BACKUP OF YOUR CONTENT FOLDER TO BE SURE TO NOT LOSE ANY DATA.
10 |
11 | Open http://yourkirbydomain.com/posterous.php in your browser to start the exporter.
12 |
13 | ## Clean-Up
14 |
15 | Make sure to remove posterous.php once you've finished exporting all your articles.
16 |
17 | ## The exporter does not…
18 |
19 | - …export images from Posterous
20 | - …convert HTML in your Posterous articles to Markdown
21 | - …export Posterous pages or custom post types
22 |
23 | ## Help needed!
24 |
25 | I could really need some help with improving the export so it will grab the images and videos and convert HTML to Markdown.
26 |
--------------------------------------------------------------------------------
/plugins/github/README.mdown:
--------------------------------------------------------------------------------
1 | GitHub Plugin for Kirby
2 | =======================
3 |
4 | With that plugin you can implement a users GitHub information or list your repos easily in Kirby.
5 |
6 | Usage example for repos:
7 | ========================
8 | ```php
9 | repos() as $repo):
13 | ?>
14 |
18 |
19 | ```
20 |
21 | Attributes for repo()
22 | =====================
23 | - `$repo->name` the name of the repository
24 | - `$repo->description` the description
25 | - `$repo->url` the url to the repository
26 | - `$repo->last_update` the timestamp to the last update
27 | - `$repo->forkcount` the number of forks
28 | - `$repo->watchers` the number of watchers
29 |
30 | Attributes for user()
31 | =====================
32 | - `$user->username` the GitHub username
33 | - `$user->name` the real name
34 | - `$user->email` the email, if given
35 | - `$user->followers` the count of followers
36 | - `$user->following` the count of following
37 | - `$user->url` the GitHub-url
38 | - `$user->gravatar_id` the gravatar-id
39 | - `$user->repos_url` the url to the repos
40 |
--------------------------------------------------------------------------------
/snippets/pagination/pagination.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exporters/wordpress/readme.mdown:
--------------------------------------------------------------------------------
1 | # Wordpress Article Exporter
2 |
3 | This exporter will grab all articles from your Wordpress blog and export them to folders and text files, which will be readable by Kirby.
4 |
5 | ## Installation
6 |
7 | Download wordpress.php and put it in the main directory of your Kirby site. Open wordpress.php in your favorite editor and setup the url to your blog and your wordpress username and password. You should also make sure that $root is setup to the folder where all the exported articles should be stored and that this folder is writable. (change permissions to 0755)
8 |
9 | MAKE A BACKUP OF YOUR CONTENT FOLDER TO BE SURE TO NOT LOSE ANY DATA.
10 |
11 | Open http://yourkirbydomain.com/wordpress.php in your browser to start the exporter.
12 |
13 | ## Clean-Up
14 |
15 | Make sure to remove wordpress.php once you've finished exporting all your articles.
16 |
17 | ## The exporter does not…
18 |
19 | - …export images from Wordpress
20 | - …convert HTML in your Wordpress articles to Markdown
21 | - …export Wordpress pages or custom post types
22 |
23 | ## Troubleshooting
24 |
25 | If the exporter keeps on failing, please try to disable all Wordpress plugins and switch to the default theme.
26 |
27 | ## Help needed!
28 |
29 | I could really need some help with improving the export so it will convert HTML to Markdown or at least clean the HTML.
30 |
31 |
--------------------------------------------------------------------------------
/plugins/highlight/readme.mdown:
--------------------------------------------------------------------------------
1 | # Highlight Plugin (Alpha)
2 |
3 | ## What is it?
4 |
5 | It's a very alpha-style code highlighting plugin, which uses Geshi highlighting to create nice code snippets for php, html, js and css. Please use it at your own risk and read the instructions carefully to install it properly.
6 |
7 | ## Installation
8 |
9 | Copy the highlight folder and highlight.php and add them to site/plugins. (Create the plugins folder if it is not there) Copy the css files from the styles folder and add them to the folder where all your css files for your site are located. Load the needed style files in your templates:
10 |
11 | Example:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## How to use it?
20 |
21 | Whenever you want to add a highlighted code snippet to your content, do it like this:
22 |
23 | ```php
24 |
25 | some php code
26 |
27 | ```
28 |
29 | ```html
30 |
31 | some html code
32 |
33 | ```
34 |
35 | ```js
36 |
37 | some javascript code
38 |
39 | ```
40 |
41 | ```css
42 |
43 | some css code
44 |
45 | ```
46 |
47 | ## Code Block styles
48 |
49 | To remove unwanted spacing you should add
50 |
51 | pre {
52 | white-space: nowrap
53 | }
54 |
55 | ## Author
56 | Bastian Allgeier
57 |
--------------------------------------------------------------------------------
/plugins/readingtime/readingtime.php:
--------------------------------------------------------------------------------
1 | text()) ?>
12 | *
13 | * Author: Roy Lodder
14 | * Contributor: Bastian Allgeier
15 | *
16 | */
17 |
18 | function readingtime($content, $params=array()) {
19 |
20 | $defaults = array(
21 | 'minute' => 'minute',
22 | 'minutes' => 'minutes',
23 | 'second' => 'second',
24 | 'seconds' => 'seconds',
25 | 'format' => '{minutesCount} {minutesLabel}, {secondsCount} {secondsLabel}'
26 | );
27 |
28 | $options = array_merge($defaults, $params);
29 | $words = str_word_count(strip_tags($content));
30 |
31 | $minutesCount = floor($words / 200);
32 | $secondsCount = floor($words % 200 / (200 / 60));
33 | $minutesLabel = ($minutesCount <= 1) ? $options['minute'] : $options['minutes'];
34 | $secondsLabel = ($secondsCount <= 1) ? $options['second'] : $options['seconds'];
35 |
36 | $replace = array(
37 | 'minutesCount' => $minutesCount,
38 | 'minutesLabel' => $minutesLabel,
39 | 'secondsCount' => $secondsCount,
40 | 'secondsLabel' => $secondsLabel,
41 | );
42 |
43 | $result = $options['format'];
44 |
45 | foreach($replace as $key => $value) {
46 | $result = str_replace('{' . $key . '}', $value, $result);
47 | }
48 |
49 | return $result;
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/plugins/related/readme.mdown:
--------------------------------------------------------------------------------
1 | # Related Pages
2 |
3 | ## What is it?
4 |
5 | This plugin makes it possible to link related pages very easily by their URI in content files.
6 |
7 | ## Installation
8 |
9 | Put the related.php file in your site/plugins folder.
10 |
11 | ## How to use it?
12 |
13 | ### In your content files
14 |
15 | Title: My title
16 | ----
17 | Text: My text
18 | ----
19 | Related:
20 |
21 | - projects/project-1
22 | - blog/my-article
23 |
24 |
25 | ### In your templates
26 |
27 | related() as $related): ?>
28 | title()) ?>
29 |
30 |
31 |
32 | ## Result
33 |
34 | The result of the related() function will be a full set of `$pages`, which you can apply the same methods to as you would to a normal set of `$pages`. This makes it possible to do stuff like:
35 |
36 | $related = related($page->related());
37 |
38 | // show the url of the first related page
39 | echo $related->first()->url();
40 |
41 | // show the title of the last related page
42 | echo $related->last()->title();
43 |
44 | // limit the number of related items
45 | foreach($related->limit(5) as $related) {
46 | // do something
47 | }
48 |
49 | // etc.
50 |
51 | For each `$page` in the set of related `$pages` you have full access to all the custom fields and methods of the related `$page`
52 |
53 |
54 | ## Author
55 | Bastian Allgeier
56 |
--------------------------------------------------------------------------------
/snippets/disqus/disqus.php:
--------------------------------------------------------------------------------
1 | title();
6 | if(!isset($disqus_developer)) $disqus_developer = false;
7 | if(!isset($disqus_identifier)) $disqus_identifier = $page->uri();
8 | if(!isset($disqus_url)) $disqus_url = thisURL();
9 |
10 | $disqus_title = addcslashes($disqus_title, "'");
11 | $disqus_developer = ($disqus_developer) ? 'true' : 'false';
12 |
13 | ?>
14 |
15 |
28 |
29 | blog comments powered by Disqus
30 |
--------------------------------------------------------------------------------
/snippets/map/readme.mdown:
--------------------------------------------------------------------------------
1 | # Google Maps Snippet
2 |
3 | ## What is it?
4 |
5 | This snippet will embed a Google Map in your site. You can set your own address, zoom level and some other options to customize it. It even has a fallback to the Google Maps Image API if javascript is disabled.
6 |
7 | ## Installation
8 |
9 | Put the map.php file in your site/snippets folder
10 |
11 | ## How to use it?
12 |
13 | 'Mannheim, Germany')) ?>
14 |
15 | …or with more options…
16 |
17 | 'Mannheim, Germany',
21 | 'zoom' => 17,
22 | 'width' => 300,
23 | 'height' => 200
24 | ));
25 |
26 | ?>
27 |
28 | ## Options
29 |
30 | ### Address (required)
31 |
32 | Set the address for your map like you would on Google Maps. For Example "Mannheim, Germany" or "Timesquare, New York"
33 |
34 | ### Width (default is 300)
35 |
36 | Set the width of your map in pixels without "px"
37 |
38 | ### Height (default is 300)
39 |
40 | Set the height of your map in pixels without "px"
41 |
42 | ### Zoom (default is 15)
43 |
44 | Set the zoom level for your map. Anything between 1 and 20 makes sense.
45 |
46 | ### Type (default is roadmap)
47 |
48 | The map type. Can be roadmap, satellite, hybrid, terrain
49 |
50 | ### ID
51 |
52 | The id of your map. By default this is set to a random string to make multiple maps possible on a single page.
53 |
54 | ### Class (default is map)
55 |
56 | Set the class name for your map container.
57 |
58 |
59 | ## Author
60 | Bastian Allgeier
61 |
--------------------------------------------------------------------------------
/snippets/contactform/readme.mdown:
--------------------------------------------------------------------------------
1 | # Contactform Plugin & Snippets (Alpha)
2 |
3 | This plugin helps you render and submit a simple contact form with name, email and text
4 |
5 | ## Installation
6 | Put the `plugins/contactform` folder in this repo in your `site/plugins` folder. If this folder doesn't exist yet, create it.
7 |
8 | ### Installing snippets
9 | Copy the snippets in this folder and add them to `site/snippets`
10 |
11 | ### Add the contact form
12 |
13 | To add the contact form to your site, add the contactform snippet to your templates:
14 |
15 | ```
16 |
17 |
18 |
19 | ```
20 |
21 | ### Contact form settings
22 |
23 | Open `site/snippets/contactform.php` to set your email address and other options for the contact form:
24 |
25 | ```
26 |
27 | $form = new contactform(array(
28 | 'to' => 'John ',
29 | 'from' => 'Contact Form ',
30 | 'subject' => 'New contact form request',
31 | 'goto' => $page->url() . '/status:thank-you'
32 | ));
33 |
34 | ```
35 |
36 | You can also customize the HTML for the contact form however you need.
37 |
38 | ### Changing the email template
39 |
40 | Open `site/snippets/contactform.mail.php` to customize the email template. The contact form plugin will only send plain text emails. You can use the following placeholders in your template:
41 |
42 | ```
43 |
44 | {name}
45 | {email}
46 | {text}
47 |
48 | ```
49 |
50 | ## Requirements
51 |
52 | Since the plugin works with PHP closures, you need to run your site on PHP 5.3 + Older versions are not supported
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/fields/tags/tags.php:
--------------------------------------------------------------------------------
1 | pages()->active();
7 |
8 | // define the separating character
9 | $separator = (isset($separator)) ? $separator : ',';
10 |
11 | // field to fetch existing tags from
12 | $field = (isset($field)) ? $field : $name;
13 |
14 | // lowercase all tags
15 | $lower = (isset($lower)) ? $lower : false;
16 |
17 | // use passed data if available or try to fetch data
18 | if(!isset($data) || !is_array($data)) {
19 |
20 | $data = array();
21 | $store = array();
22 |
23 | if(!isset($index)) $index = 'siblings';
24 |
25 | switch($index) {
26 | case 'template':
27 | foreach($site->pages()->index() as $p) {
28 | if($p->template() == $page->template()) $store[] = $p;
29 | }
30 | break;
31 | case 'all':
32 | $store = $site->pages()->index();
33 | break;
34 | case 'siblings':
35 | $store = $page->siblings();
36 | break;
37 | }
38 |
39 | // get all tags
40 | foreach($store as $s) {
41 | $data = array_merge($data, str::split($s->{$field}, $separator));
42 | }
43 |
44 | }
45 |
46 | // make sure we get a nice array
47 | $data = array_values(array_unique($data));
48 | sort($data);
49 |
50 | ?>
51 |
52 |
53 |
62 |
--------------------------------------------------------------------------------
/plugins/tagcloud/tagcloud.php:
--------------------------------------------------------------------------------
1 | false,
10 | 'field' => 'tags',
11 | 'children' => 'visible',
12 | 'baseurl' => $parent->url(),
13 | 'param' => 'tag',
14 | 'sort' => 'results',
15 | 'sortdir' => 'desc'
16 | );
17 |
18 | // merge defaults and options
19 | $options = array_merge($defaults, $options);
20 |
21 | switch($options['children']) {
22 | case 'invisible':
23 | $children = $parent->children()->invisible();
24 | break;
25 | case 'visible':
26 | $children = $parent->children()->visible();
27 | break;
28 | default:
29 | $children = $parent->children();
30 | break;
31 | }
32 |
33 | $cloud = array();
34 | $ds = DIRECTORY_SEPARATOR == '/' ? ':' : ';';
35 |
36 | foreach($children as $p) {
37 |
38 | $tags = str::split($p->$options['field']());
39 |
40 | foreach($tags as $t) {
41 |
42 | if(isset($cloud[$t])) {
43 | $cloud[$t]->results++;
44 | } else {
45 | $cloud[$t] = new obj(array(
46 | 'results' => 1,
47 | 'name' => $t,
48 | 'url' => $options['baseurl'] . '/' . $options['param'] . $ds . $t,
49 | 'isActive' => (param($options['param']) == $t) ? true : false,
50 | ));
51 | }
52 |
53 | }
54 |
55 | }
56 |
57 | $cloud = a::sort($cloud, $options['sort'], $options['sortdir']);
58 |
59 | if($options['limit']) {
60 | $cloud = array_slice($cloud, 0, $options['limit']);
61 | }
62 |
63 | return $cloud;
64 |
65 | }
--------------------------------------------------------------------------------
/plugins/contactform/readme.mdown:
--------------------------------------------------------------------------------
1 | # Contactform Plugin & Snippets (Alpha)
2 |
3 | This plugin helps you render and submit a simple contact form with name, email and text
4 |
5 | ## Installation
6 | Put the `contactform` folder in your `site/plugins` folder. If the `site/plugins` folder doesn't exist yet, create it.
7 |
8 | ### Installing snippets
9 | Copy the snippets from `snippets/contactform` in this repo and add them to `site/snippets`, don't create a subfolder in `site/snippets`.
10 |
11 | ### Add the contact form
12 |
13 | To add the contact form to your site, add the contactform snippet to your templates:
14 |
15 | ```
16 |
17 |
18 |
19 | ```
20 |
21 | ### Contact form settings
22 |
23 | Open `site/snippets/contactform.php` to set your email address and other options for the contact form:
24 |
25 | ```
26 |
27 | $form = new contactform(array(
28 | 'to' => 'John ',
29 | 'from' => 'Contact Form ',
30 | 'subject' => 'New contact form request',
31 | 'goto' => $page->url() . '/status:thank-you'
32 | ));
33 |
34 | ```
35 |
36 | You can also customize the HTML for the contact form however you need.
37 |
38 | ### Changing the email template
39 |
40 | Open `site/snippets/contactform.mail.php` to customize the email template. The contact form plugin will only send plain text emails. You can use the following placeholders in your template:
41 |
42 | ```
43 |
44 | {name}
45 | {email}
46 | {text}
47 |
48 | ```
49 |
50 | ## Requirements
51 |
52 | Since the plugin works with PHP closures, you need to run your site on PHP 5.3 + Older versions are not supported
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/plugins/readlater/readlater.php:
--------------------------------------------------------------------------------
1 | array(
7 | 'link' => 'http://www.instapaper.com/hello2?url={url}&title={title}',
8 | 'label' => 'Add to Instapaper',
9 | ),
10 | 'pocket' => array(
11 | 'link' => 'https://getpocket.com/save?url={url}&title={title}',
12 | 'label' => 'Save to Pocket',
13 | ),
14 | 'readability' => array(
15 | 'link' => 'http://www.readability.com/save?url={url}',
16 | 'label' => 'Add to Readability',
17 | )
18 | );
19 |
20 | if(!array_key_exists($tool, $tools)) return false;
21 |
22 | // make it possible to pass params
23 | // as third object instead of fourth.
24 | if(is_array($label)) {
25 | $params = $label;
26 | $label = false;
27 | }
28 |
29 | $defaults = array(
30 | 'label' => $label,
31 | 'rel' => false,
32 | 'target' => false,
33 | 'class' => 'readlater ' . $tool,
34 | 'url' => url::current(),
35 | );
36 |
37 | $options = array_merge($defaults, $params);
38 | $url = urlencode($options['url']);
39 | $title = urlencode($content);
40 | $label = ($options['label']) ? $options['label'] : $tools[$tool]['label'];
41 | $rel = ($options['rel']) ? ' rel="' . $options['rel'] . '"' : '';
42 | $target = ($options['target']) ? ' target="' . $options['target'] . '"' : '';
43 | $class = ($options['class']) ? ' class="' . $options['class'] . '"' : '';
44 |
45 | $link = str_replace('{url}', $url, $tools[$tool]['link']);
46 | $link = str_replace('{title}', $title, $link);
47 |
48 | return '' . $label . '';
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/plugins/related/related.php:
--------------------------------------------------------------------------------
1 | related() as $related): ?>
23 | * title()) ?>
24 | *
25 | *
26 | * Author: Bastian Allgeier
27 | *
28 | */
29 |
30 | function related($field) {
31 |
32 | global $site;
33 |
34 | // parse the field with yaml
35 | $raw = yaml($field);
36 | $related = array();
37 | $pages = $site->pages();
38 |
39 | foreach($raw as $r) {
40 | // make sure to only add found related pages
41 | if($rel = $pages->find($r)) $related[] = $rel;
42 | }
43 |
44 | return new relatedPages($related);
45 |
46 | }
47 |
48 | // this is only needed to build a proper find method
49 | // the pages find method will be broken with pages from
50 | // various subfolders
51 | class relatedPages extends pages {
52 |
53 | function find() {
54 |
55 | global $site;
56 |
57 | $args = func_get_args();
58 |
59 | // find multiple pages
60 | if(count($args) > 1) {
61 | $result = array();
62 | foreach($args as $arg) {
63 | $page = $this->find($arg);
64 | if($page) $result[] = $page;
65 | }
66 | return (empty($result)) ? false : new relatedPages($result);
67 | }
68 |
69 | return $site->pages()->find(a::first($args));
70 |
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/fields/fileselect/readme.mdown:
--------------------------------------------------------------------------------
1 | # Kirby Panel Fileselect field
2 |
3 | ## What is this
4 |
5 | This is a custom fileselect field for the Kirby panel, which makes it easy to select an uploaded file from the current page and add it to your content txt as a separate field.
6 |
7 | ## Installation
8 |
9 | If not already exists, add a fields folder to your `site/panel` folder and copy the entire tags field folder there. Your structure should look like this:
10 |
11 | site
12 | panel
13 | fields
14 | fileselect
15 | fileselect.css
16 | fileselect.php
17 |
18 | ## In your blueprints
19 |
20 | As soon as you dropped the fileselect field into your panel folder you can use it in your blueprints:
21 |
22 | fields:
23 | file:
24 | label: My File
25 | type: fileselect
26 |
27 | This will render a select field with all available files for the current page.
28 |
29 | ## Options
30 |
31 | ### empty
32 |
33 | Add a first "empty" option to the select field with a custom label. This makes it possible to not select a file at all.
34 |
35 | #### Example
36 |
37 | fields:
38 | file:
39 | label: My File
40 | type: fileselect
41 | empty: Select a file
42 |
43 |
44 | ### filetype
45 |
46 | Filter the list of files, which are used for the select field by filetype. Available filetypes are image, video, document, sound, other. You can specify more than just one type
47 |
48 | #### Example
49 |
50 | fields:
51 | file:
52 | label: My File
53 | type: fileselect
54 | filetype:
55 | - document
56 |
57 | ### extension
58 |
59 | Filter the list of files, which are used for the select field by extension. You can specify more than just on extension and even combine this with filetype filtering for more control
60 |
61 | #### Example
62 |
63 | fields:
64 | file:
65 | label: My File
66 | type: fileselect
67 | extension:
68 | - jpg
69 | - png
--------------------------------------------------------------------------------
/plugins/readingtime/readme.mdown:
--------------------------------------------------------------------------------
1 | # Estimated reading time plugin
2 |
3 | This is a plugin for [Kirby](http://getkirby.com/) that estimates the reading time for your content.
4 |
5 | ## Installation
6 |
7 | Put the `readingtime.php` file in your `site/plugins` folder.
8 |
9 | ## How to use it
10 |
11 | It's very simple! Just put `text()) ?>` in your template and it will echo the estimated reading time. You can pass any other page variable to estimate the reading time of it.
12 |
13 | ## Example usage
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
title()) ?>
23 | text()) ?>
24 | text()) ?>
25 |
26 |
27 |
28 |
29 |
30 |
31 | ## Language
32 |
33 | The plugin is very easy to localize. The default format for example is 3 minutes, 31 seconds. Pass the following params to translate it to any language:
34 |
35 | text(), array(
38 | 'minute' => 'Minute',
39 | 'minutes' => 'Minuten',
40 | 'second' => 'Sekunde',
41 | 'seconds' => 'Sekunden'
42 | ));
43 |
44 | ?>
45 |
46 | You can even change the entire format of the result:
47 |
48 | text(), array(
51 | 'minute' => 'Minute',
52 | 'minutes' => 'Minuten',
53 | 'second' => 'Sekunde',
54 | 'seconds' => 'Sekunden',
55 | 'format' => '{minutesCount} {minutesLabel} – {secondsCount} {secondsLabel}
56 | ));
57 |
58 | ?>
59 |
60 | ## Author
61 |
62 | Author: Roy Lodder
63 | Contributor: Bastian Allgeier
64 |
65 | Original Version: https://github.com/Roylodder/Kirbycms-Reading-Time-Snippet
66 |
67 |
--------------------------------------------------------------------------------
/plugins/readlater/readme.mdown:
--------------------------------------------------------------------------------
1 | # Read later plugin
2 |
3 | This is a plugin for [Kirby](http://getkirby.com/) that let´s you save the page for later reading. It currently supports [Instapaper](http://www.instapaper.com/) and [Pocket](http://getpocket.com/).
4 |
5 | ## Installation
6 |
7 | Put the `readlater.php` file in your `site/plugins` folder.
8 |
9 | ## How to use it
10 |
11 | Simple! Put `title(),'instapaper') ?>` in your template and it will echo the a link that let´s you save the page to Instapaper. Do you want to use Pocket? Then you use `title(),'pocket') ?>` in your template.
12 |
13 | You can pass any other page variable to use for the title. I asume that you use `$page->title()` for the title but you are free to change it.
14 |
15 | ## Example usage
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | title(),'pocket') ?>
25 |
title()) ?>
26 | text()) ?>
27 |
28 |
29 |
30 |
31 |
32 |
33 | ## Link text
34 |
35 | The plugin let's you change the link text with ease. The default format for Instapaper is `Add to Instapaper`. For Pocket we use `Save to Pocket`.
36 |
37 | Simply pass the link text as third argument to use your own.
38 |
39 | title(), 'instapaper', 'Read later'); ?>
40 |
41 | ## Options
42 |
43 | You can add additional options to customize the html result:
44 |
45 | title(), 'instapaper', 'Read later', array(
48 | 'class' => 'mycustomclass',
49 | 'target' => '_blank',
50 | 'rel' => 'somerelattribute'
51 | ));
52 |
53 | ?>
54 |
55 | ## Author
56 |
57 | Author: Roy Lodder
58 | Contributor: Bastian Allgeier
--------------------------------------------------------------------------------
/snippets/feed/feed.php:
--------------------------------------------------------------------------------
1 | ';
11 |
12 | ?>
13 |
14 |
15 |
16 |
17 |
18 | title()) ?>
19 |
20 |
21 | modified()) ?>
22 |
23 |
24 | description() || isset($description)): ?>
25 | description()) ?>
26 |
27 |
28 |
29 |
30 | title()) ?>
31 | url()) ?>
32 | url()) ?>
33 | date()) ? date('r', $item->date()) : date('r', $item->modified()) ?>
34 |
35 |
36 |
37 | {$descriptionField}) ?>]]>
38 |
39 | {$descriptionField}, (isset($descriptionLength)) ? $descriptionLength : 140) ?>]]>
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/plugins/tagcloud/readme.mdown:
--------------------------------------------------------------------------------
1 | # Tagcloud Plugin (Beta)
2 |
3 | ## What is it?
4 |
5 | The tagcloud plugin will fetch all tags (comma separated) from subpages and return them with additional information - like the number of subpages for a each tag.
6 |
7 | ## Installation
8 |
9 | Put the tagcloud.php file in your site/plugins folder.
10 |
11 | ## How to use it?
12 |
13 | ### Content files
14 |
15 | To add tags to your content files, put them in a comma separated list:
16 |
17 | Title: Some Title
18 | ----
19 | Text: Some Text
20 | ----
21 | Tags: design, fun, photography
22 | ----
23 |
24 | In your template add i.e.:
25 |
26 | find('blog'), array('limit' => 20)) ?>
27 |
28 |
33 |
34 |
35 | As first argument you pass the main page. All children of it will be searched to find all tags. So if you have a blog and you want to find all tags of blog articles you add pass the main blog page as first argument.
36 |
37 |
38 | ## Available options
39 |
40 | ### limit
41 |
42 | The number of tags in your tagcloud. If not specified, all tags will be returned.
43 |
44 | ### field
45 |
46 | The name of the Tags field in your content files. By default this is "tags"
47 |
48 | ### children
49 |
50 | Which children of the given page should be searched? Possible values are "all", "visible", "invisible". Default is "visible"
51 |
52 | ### baseurl
53 |
54 | The base URL, which will be used to build tag URLs. By default the URL of the given page.
55 |
56 | ### param
57 |
58 | The name of the param, which will be used for the URL. By default this is just "tag". So the default URL will be "http://yourdomain.com/given-page/tag:design" for example
59 |
60 | ### sort
61 |
62 | The field to sort by. By default the tagcloud will be sorted by the number of results per tag, but you can also sort it by tag name for example.
63 |
64 | ### sortdir
65 |
66 | The sort direction. Can be "desc" or "asc". Default is "desc"
67 |
68 |
69 | ## Author
70 | Bastian Allgeier
71 |
--------------------------------------------------------------------------------
/plugins/search/readme.mdown:
--------------------------------------------------------------------------------
1 | # Search Plugin (Beta)
2 |
3 | ## What is it?
4 |
5 | The search plugin makes it possible to search the entire content of your site and to build a nice search form for your users.
6 |
7 | ## Installation
8 |
9 | Put the search.php file in your site/plugins folder.
10 |
11 | ## How to use it?
12 |
13 | $search = new search(array('searchfield' => 'search'));
14 | $results = $search->results();
15 |
16 | ## Available options
17 |
18 | ### searchfield
19 |
20 | The name of the html search field. This will also build the param in your URL:
21 | http://yourdomain.com/search/fieldname:searchword
22 |
23 | ### query
24 |
25 | Instead of passing a search field you can also pass a query string instead, which will be used to search for. The search won't be connected to a search form that way, but you can use it to manually search by that query.
26 |
27 | ### fields
28 |
29 | An array of field names to search in. By default all fields will be searched.
30 |
31 | ### mode ("and" or "or" | default is "or")
32 |
33 | Switch between "and" or "or" as search mode.
34 |
35 | ### score
36 |
37 | An array with field names as keys and score values, which will be mulitplied with the hits per field to get a more valid result
38 |
39 | ### words (default = false)
40 |
41 | Set this to true to search for full words only instead of strings within words.
42 |
43 | ### ignore
44 |
45 | An array of uris which should be ignored. This is perfect to not show "secret" pages in results or to skip the error page for example.
46 |
47 | ### include
48 |
49 | The include option makes it possible to take control of which pages are included in search results. You get the following options: "all", "visible", "invisible"
50 |
51 | ### in
52 |
53 | Specify a URI here to search within the children of that page instead of the entire site.
54 |
55 | ### minlength
56 |
57 | The minimum length of searchwords.
58 |
59 | ### stopwords
60 |
61 | An array of stopwords, which will not be considered during search.
62 |
63 | ### paginate
64 |
65 | Automatically paginate the final result. Add a number of rows to return for each result page.
66 |
67 | ## Author
68 | Bastian Allgeier
69 |
--------------------------------------------------------------------------------
/snippets/feed/readme.mdown:
--------------------------------------------------------------------------------
1 | # Feed Snippet
2 |
3 | ## What is it?
4 |
5 | A very simple snippet to help you setup RSS-Feeds for your site.
6 |
7 | ## Installation
8 |
9 | Put the feed.php file in your site/snippets folder
10 |
11 | ## How to use it?
12 |
13 | find('blog')->children()->visible()->flip()->limit(10);
19 |
20 | // this is how you embed the feed snippet with some options
21 | snippet('feed', array(
22 | 'link' => url('blog'),
23 | 'items' => $items,
24 | 'descriptionField' => 'text',
25 | 'descriptionLength' => 300
26 | ));
27 |
28 | ?>
29 |
30 | ## Available Options
31 |
32 | ### title
33 |
34 | You can specify a title for your feed here. Otherwise the title, which you've added to your content txt (in our case feed.txt) will be used
35 |
36 | ### description
37 |
38 | You can specify a description for your feed here. Otherwise the description, which you've added to your content txt (in our case feed.txt) will be used
39 |
40 | ### link
41 |
42 | Main link in your feed to your site. In this example we want to link to our blog page.
43 |
44 | ### modified
45 |
46 | If you want to specify a modified date, simply pass a timestamp here. Otherwise this will be auto generated by the last modified date of your entire site.
47 |
48 | ### items
49 |
50 | Any set of pages
51 |
52 | ### descriptionExcerpt
53 |
54 | Create an excerpt – which you can control with descriptionLength – or not. This is true by default.
55 |
56 | ### descriptionField
57 |
58 | If you want to show a description for each item in your feed, make sure to specify a field, which is available in any item and should be used for the description. For example every page of our blog has a text field, so we use that for the item description.
59 |
60 | ### descriptionLength
61 |
62 | This is maximum number of characters the description will have. An excerpt is automatically generated.
63 |
64 | ## Author
65 | Bastian Allgeier
66 |
67 |
--------------------------------------------------------------------------------
/plugins/thumb/readme.mdown:
--------------------------------------------------------------------------------
1 | # Thumb Plugin
2 |
3 | ## What is it?
4 |
5 | The thumb plugin makes it easy to include smaller, resized versions of your images in your pages without adding extra code or uploading smaller versions yourself.
6 |
7 | ## Installation
8 |
9 | Put the thumb.php file in your site/plugins folder. Add the plugins folder if it isn't there yet. Add a thumbs folder to the root dir of your site and make sure it is writable. If you want to change the location of your thumbs cache folder read more about the available default settings further down.
10 |
11 | ## How to use it?
12 |
13 | 200, 'height' => 150)); ?>
14 |
15 | or a more extended version:
16 |
17 | thumb($image, array(
18 | 'width' => 200,
19 | 'height' => 200,
20 | 'quality' => 90,
21 | 'upscale' => true,
22 | 'crop' => true,
23 | 'grayscale' => true
24 | ));
25 |
26 | ## Available options
27 |
28 | ### width
29 |
30 | set the maximum width of your image
31 |
32 | ### height
33 |
34 | set the maximum height of your image
35 |
36 | ### quality
37 |
38 | set the jpeg compression quality of your image. (0-100)
39 |
40 | ### upscale
41 |
42 | If this is set to true, images will be upscaled to the maximum height or width.
43 |
44 | ### crop
45 |
46 | Set this to true to crop the image to the given width and height. Upscale is true by default in this case.
47 |
48 | ### grayscale
49 |
50 | If set to true RGB images will be converted to grayscale
51 |
52 | ### datauri
53 |
54 | If set to true image URLs will be converted to data:URIs
55 |
56 | ## Available settings
57 |
58 | You can add the following config variables to your config file (site/config/config.php) to adjust the default settings of the thumb plugin:
59 |
60 | c::set('thumb.cache.root', c::get('root') . '/thumbs');
61 | c::set('thumb.cache.url', c::get('url') . '/thumbs');
62 | c::set('thumb.quality', 100);
63 | c::set('thumb.upscale', false);
64 | c::set('thumb.datauri', false);
65 |
66 | ## Requirements
67 |
68 | You must have GD Lib installed on your server to make this work.
69 |
70 | ## Author
71 | Bastian Allgeier
72 |
--------------------------------------------------------------------------------
/snippets/contactform/contactform.php:
--------------------------------------------------------------------------------
1 | 'John ',
5 | 'from' => 'Contact Form ',
6 | 'subject' => 'New contact form request',
7 | 'goto' => $page->url() . '/status:thank-you'
8 | ));
9 |
10 | ?>
11 |
12 |
13 |
14 |
15 |
34 |
35 |
36 | ## Available Options
37 |
38 | Set options like this:
39 |
40 | 5,
44 | 'refresh' => 60*60*5
45 | ));
46 |
47 | ?>
48 |
49 | ### limit (default: 10)
50 |
51 | Sets the number of returned tweets.
52 |
53 | ### cache (default: true)
54 |
55 | Disable or enable the cache. Caching the results is highly recommended. Otherwise the plugin will be super slow. You must make sure that `site/cache` is writable (change permissions to 0755) You must also set c::set('cache', true); in site/config/config.php to enable the cache.
56 |
57 | ### hiderep (default: false)
58 |
59 | Show or hide replies you've tweeted, e.g. for users with a large number of replies that aren't interesting for the general public.
60 |
61 | ### refresh (default: 20 minutes)
62 |
63 | Set the cache expiry in seconds. By default it will be set to 20 minutes. After 20 minutes the plugin will fetch fresh data from the Twitter API.
64 |
65 |
66 | ## Attributes for the tweet-object
67 |
68 | * `$tweet->url()` The URL to the Tweet page on Twitter
69 | * `$tweet->text()` The text of the Tweet
70 | * `$tweet->date()` The timestamp when the Tweet has been posted
71 | * `$tweet->source()` The source, from which the Tweet has been posted
72 | * `$tweet->user()` The user object (see object attributes)
73 |
74 | ## Attributes for the user-object
75 |
76 | * `$tweet->user()->name()` The full name of the user
77 | * `$tweet->user()->username()` The Twitter username
78 | * `$tweet->user()->bio()` The Twitter bio
79 | * `$tweet->user()->url()` The URL to the user's Twitter page
80 | * `$tweet->user()->image()` The user avatar pic
81 | * `$tweet->user()->following()` The number of accounts the user is following
82 | * `$tweet->user()->followers()` The number of followers
83 |
84 |
85 | ## Author
86 | Bastian Allgeier
87 |
88 |
--------------------------------------------------------------------------------
/snippets/map/map.php:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
71 |
72 |
83 |
--------------------------------------------------------------------------------
/fields/tags/readme.mdown:
--------------------------------------------------------------------------------
1 | # Kirby Panel Tag field
2 |
3 | ## What is this
4 |
5 | This is a custom tag field for the Kirby panel, which makes it easy to add multiple tags or other kinds of multiple entries to a single Kirby page. Content will be added in a comma-separated list. The tag field includes a nice javascript plugin, which is similar to Facebook's User input or Tags on the Mac. It also includes autocompletion.
6 |
7 | ## Installation
8 |
9 | If not already exists, add a fields folder to your `site/panel` folder and copy the entire tags field folder there. Your structure should look like this:
10 |
11 | site
12 | panel
13 | fields
14 | tags
15 | tags.css
16 | tags.js
17 | tags.php
18 |
19 | ## In your blueprints
20 |
21 | As soon as you dropped the tags field into your panel folder you can use it in your blueprints:
22 |
23 | fields:
24 | tags:
25 | label: My Tags
26 | type: tags
27 |
28 | This will render the default tags field and will look at sibling pages for matching tag suggestions. You got a bunch of options to customize the field though.
29 |
30 | ## Options
31 |
32 | ### index
33 |
34 | You can choose between three different options where to get tag suggestions from:
35 |
36 | 1. siblings
37 |
38 | The plugin will check all siblings in the same parent folder to look for tag suggestions. It will collect a unique list of tags from all the other pages
39 |
40 | 2. template
41 |
42 | The plugin will check through all pages of your site and select tags from those with the same template – no matter where they are located. It will collect a unique list of tags.
43 |
44 | 3. all
45 |
46 | The plugin will fetch tags from all pages
47 |
48 | #### Example
49 |
50 | fields:
51 | tags:
52 | label: My Tags
53 | type: tags
54 | index: template
55 |
56 |
57 | ### field
58 |
59 | You can choose which field will be used to get tags from other pages. This makes it possible to not only use the tags field for – well, tags – but for all kinds of data from your site
60 |
61 | #### Example
62 |
63 | fields:
64 | tags:
65 | label: My Tags
66 | type: tags
67 | index: template
68 | field: title
69 |
70 | (This will pull titles from all pages with the same template as auto-suggestions)
71 |
72 | ### lower
73 |
74 | For tags it's often useful to automatically lowercase all tags. You can use this option to do that:
75 |
76 | #### Example
77 |
78 | fields:
79 | tags:
80 | label: My Tags
81 | type: tags
82 | lower: true
83 |
84 | (the resulting tag string will be completely lowercase)
85 |
86 | ### Separator
87 |
88 | Maybe you don't want to separate your data by comma. Set the separator character here.
89 |
90 | #### Example
91 |
92 | fields:
93 | tags:
94 | label: My Tags
95 | type: tags
96 | separator: ;
97 |
98 | ### data
99 |
100 | If you don't want to fetch suggestions from other pages, you can set your own array of strings, which will be used for autocompletion
101 |
102 | #### Example
103 |
104 | fields:
105 | tags:
106 | label: My Tags
107 | type: tags
108 | data:
109 | - design
110 | - architecture
111 | - photography
112 | - development
113 | - web
114 |
115 |
--------------------------------------------------------------------------------
/plugins/sublimevideo/readme.mdown:
--------------------------------------------------------------------------------
1 | # SublimeVideo
2 |
3 | ## What is it?
4 |
5 | This plugin is a Kirbytext extension, in order to easily use the [SublimeVideo HTML5 Player](http://sublimevideo.net/). It provides you with a new tag:
6 |
7 | (sublime: video-name width: xxx height: yyy)
8 |
9 | ## Installation
10 |
11 | 1. Copy the `sublimevideo.php` file into your `site/plugins` folder. If the plugins folder doesn't exist yet, please create it. As a plugin, the file will be automatically loaded by Kirby.
12 |
13 | 2. Put your SublimeVideo embed code inside the `` tag of your site. It should look like this :
14 |
15 | ```html
16 |
17 | ```
18 |
19 | Please note that you need to have a [SublimeVideo account](http://sublimevideo.net/plans) (free). This service is very easy to use but if you need help, please refer to [their documentation](http://docs.sublimevideo.net/quickstart-guide). I am also not going to discuss [video encoding](http://docs.sublimevideo.net/encode-videos-for-the-web) here.
20 |
21 | ## How to use it?
22 |
23 | This tag will load all video files and an image poster file from the current page folder, which match the video-name value. Here's a list of example files:
24 |
25 | - myvideo.hd.mp4
26 | - myvideo.hd.ogv
27 | - myvideo.hd.webm
28 | - myvideo.mobile.mp4
29 | - myvideo.mobile.ogv
30 | - myvideo.mobile.webm
31 | - myvideo.poster.png
32 | - myvideo.standard.mp4
33 | - myvideo.standard.ogv
34 |
35 | Use the tag…
36 |
37 | (sublime: myvideo width: 400 height: 300)
38 |
39 | …in your text to automatically load the SublimeVideo with all matching files.
40 |
41 | Adding `.hd` and `.standard` to files with the same extension will mark files as HD and low-res video files and will enable [SublimeVideo HD switching](http://docs.sublimevideo.net/hd-switching) You can also add the `.mobile` prefix to specify a file, which will be used on mobile devices.
42 |
43 | If you add an additional image file with the same naming convention (i.e. `myvideo.poster.png`) this file will be used as a preview as long as the video has not been loaded yet and if video playback is not available.
44 |
45 | ### Attributes
46 |
47 | - `sublime`: the files common name
48 | - `width`: the width of the video
49 | - `height`: the height of the video
50 | - `uid`: a unique id (optional), which will appear in the [SublimeVideo stats](http://docs.sublimevideo.net/optimize-for-stats)
51 | - `name`: the name of the video (optional), which will appear in the [SublimeVideo stats](http://docs.sublimevideo.net/optimize-for-stats)
52 | - `class`: an additional class name, which will be added to the video tag. SublimeVideo tags already have a sublime class by default, but you can use this optionally to add another class.
53 |
54 | ### Full Example
55 |
56 | (sublime: myvideo width: 540 height: 304 name: My awesome video uid: myvideo class: myvideo)
57 |
58 | ## Shortcut
59 |
60 | If you need to embed SublimeVideos directly in your template or snippet files you can use the available shortcut function:
61 |
62 | ```php
63 |
64 |
65 |
66 | ```
67 |
68 | ## Support
69 |
70 | Provided as is, no support provided
71 |
72 | ## Author
73 |
74 | Author: Thibaut Ninove ([WooConcept](http://wooconcept.com))
75 | Contributor: Bastian Allgeier
--------------------------------------------------------------------------------
/plugins/instagram/readme.mdown:
--------------------------------------------------------------------------------
1 | # Instagram Plugin for Kirby by Simon Albrecht (1.0)
2 | This is a plugin for [Kirby](http://getkirby.com/) that loads images from the [Instagram](http://instagram.com/) API.
3 |
4 | ## Installation
5 | 1. Put the `instagram.php` file in your `site/plugins` folder. If this folder doesn't exist yet, create it.
6 | 2. In order to interact with the Instagram API, you need to obtain an access token for yourself.
7 | 3. Visit and register an application.
8 | 4. Set the OAuth redirect_uri to the main URL (i.e. http://yourdomain.com) of your website.
9 | 5. Copy the Client-ID of the newly created app.
10 | 6. Visit:
11 |
12 | https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=http://yourdomain.com&response_type=token
13 |
14 | …in your browser, but replace CLIENT-ID with your client-id and YOURDOMAIN.COM with the OAuth redirect_uri you've entered while registering your app.
15 |
16 | 7. The browser will redirect you to your own website and in the address bar you will find your access token like this:
17 |
18 | http://yourdomain.com/#access_token=xxxxx.xxxxx.xxxxxxxxxxx
19 |
20 | 8. Copy the access token (everything after =) and save it somewhere.
21 | 9. Implement the plugin into your template.
22 |
23 | ## Update instructions
24 | To update, just replace the old `instagram.php` file in `site/plugins`, with the new one.
25 |
26 | ## Example usage
27 | images();
33 |
34 | foreach ($images as $image): ?>
35 |
43 |
44 |
45 | **Advanced Users:** See the source for further options.
46 |
47 | ## Attributes for the image
48 | * `$image->link` The link to the image
49 | * `$image->comments` The number of comments
50 | * `$image->likes` The number of likes
51 | * `$image->created` The timestamp when the image was created
52 | * `$image->thumb` The url of the thumbnail of the image
53 | * `$image->url` The url of the full-sized image
54 | * `$image->image_lowres` The url to a low-res version of the image
55 | * `$image->filter` The filter that was used
56 | * `$image->location` The location name
57 | * `$image->latitude` The latitude of the location
58 | * `$image->longitude` The longitude of the location
59 | * `$image->tags` An array of tags of the image
60 |
61 | ## Attributes for the user-object
62 | * `$user->username` The username of the user
63 | * `$user->full_name` The full name of the user
64 | * `$user->picture` The url to the avatar of the user
65 |
66 | ## Requirements
67 | Your web server must have support for cURL [(installation instructions)](http://www.php.net/manual/en/curl.installation.php).
68 |
69 | ## Author
70 | Copyright 2012, Simon Albrecht [http://albrecht.me/](http://albrecht.me).
71 | If you use this plugin, feel free to ping me [@s_albrecht](http://twitter.com/s_albrecht).
--------------------------------------------------------------------------------
/snippets/flattr/flattr.php:
--------------------------------------------------------------------------------
1 | url();
9 | if(!isset($title)) $title = $page->title();
10 | if(!isset($description)) $description = null;
11 | if(!isset($category)) $category = null;
12 | if(!isset($language)) $language = null;
13 | if(!isset($tags)) $tags = null;
14 | if(!isset($button)) $button = null;
15 | if(!isset($popout)) $popout = null;
16 | if(!isset($hidden)) $hidden = null;
17 | if(!isset($html5)) $html5 = false;
18 |
19 | if(!$instances) :
20 |
21 | // javascript output (once)
22 | ?>
23 |
37 | endif;
38 |
39 | // html5 output
40 | if($html5 != false) :
41 | $atts = ' data-flattr-uid="' . $user_id . '"';
42 | if(!is_null($description)) $atts .= ' data-flattr-description="' . $description . '"';
43 | if(!is_null($category)) $atts .= ' data-flattr-category="' . $category . '"';
44 | if(!is_null($language)) $atts .= ' data-flattr-language="' . $language . '"';
45 | if(!is_null($tags)) $atts .= ' data-flattr-tags="' . $tags . '"';
46 | if(!is_null($button)) $atts .= ' data-flattr-button="compact"';
47 | if(!is_null($popout)) $atts .= ' data-flattr-popout="0"';
48 | if(!is_null($hidden)) $atts .= ' data-flattr-hidden="1"';
49 | ?>
50 | href=" echo $url ?>">Flattr this!
51 |
64 | Flattr this!
65 | endif;
66 |
67 | // noscript output
68 | $nosurl = 'https://flattr.com/submit/auto?user_id=' . $user_id . '&url=' . urlencode($url);
69 | if(!is_null($title)) $nosurl .= '&title=' . urlencode($title);
70 | if(!is_null($description)) $nosurl .= '&description=' . urlencode($description);
71 | if(!is_null($category)) $nosurl .= '&category=' . urlencode($category);
72 | if(!is_null($language)) $nosurl .= '&language=' . urlencode($language);
73 | if(!is_null($tags)) $nosurl .= '&tags=' . urlencode(implode(',', $tags));
74 | if(!is_null($hidden)) $nosurl .= '&hidden=1';
75 | ?>
76 |
--------------------------------------------------------------------------------
/plugins/sublimevideo/sublimevideo.php:
--------------------------------------------------------------------------------
1 | , with the help of Bastian Allgeier
6 | // Support: provided as is, no support provided
7 |
8 |
9 | // shortcut function to add sublime videos to templates and snippets as well.
10 | function sublime($id, $width=false, $height=false, $uid=false, $name=false, $class=false) {
11 | $kirbytext = kirbytext::classname();
12 | $obj = new $kirbytext();
13 | return $obj->sublime(array(
14 | 'sublime' => $id,
15 | 'width' => $width,
16 | 'height' => $height,
17 | 'uid' => ($uid) ? $uid : $id,
18 | 'name' => ($name) ? $name : $id,
19 | 'class' => $class
20 | ));
21 | }
22 |
23 |
24 | // kirbytext extension
25 | class kirbytextExtended extends kirbytext {
26 |
27 | function __construct($text=false, $markdown=true) {
28 |
29 | parent::__construct($text, $markdown);
30 |
31 | $this->addTags('sublime');
32 | $this->addAttributes('uid', 'name');
33 |
34 | }
35 |
36 | function sublime($params) {
37 |
38 | global $site;
39 |
40 | $page = ($this->obj) ? $this->obj : $site->pages()->active();
41 | $id = @$params['sublime'];
42 | $class = @$params['class'];
43 | $videos = array();
44 | $poster = false;
45 |
46 | // gather all video files which match the given id/name
47 | foreach($page->videos() as $v) {
48 |
49 | if(preg_match('!^' . preg_quote($id) . '!i', $v->name())) {
50 |
51 | $extension = f::extension($v->name());
52 | $mobile = ($extension == 'mobile') ? $v->mobile = true : $v->mobile = false;
53 | $hd = ($extension == 'hd') ? $v->hd = true : $v->hd = false;
54 |
55 | $videos[] = $v;
56 |
57 | }
58 |
59 | }
60 |
61 | if(empty($videos)) return false;
62 |
63 | // find the poster for this video
64 | foreach($page->images() as $i) {
65 | if(preg_match('!^' . preg_quote($id) . '!i', $i->name())) {
66 | $poster = $i;
67 | break;
68 | }
69 | }
70 |
71 | $defaults = array(
72 | 'width' => 400,
73 | 'height' => 300,
74 | 'uid' => $id,
75 | 'name' => $id,
76 | );
77 |
78 | $options = array_merge($defaults, $params);
79 | $width = html($options['width']);
80 | $height = html($options['height']);
81 | $uid = html($options['uid']);
82 | $name = html($options['name']);
83 |
84 | if(!$width) $width = c::get('kirbytext.video.width');
85 | if(!$height) $height = c::get('kirbytext.video.height');
86 |
87 | // create an additional css class if specified
88 | if(!empty($class)) $class = ' ' . html($class);
89 |
90 | // check for a poster
91 | $poster = ($poster) ? ' poster="' . $poster->url() . '"' : false;
92 |
93 | $html = '';
101 |
102 | return $html;
103 |
104 | }
105 |
106 | }
--------------------------------------------------------------------------------
/fields/tags/tags.css:
--------------------------------------------------------------------------------
1 | /* autocomplete style */
2 | .autocomplete {
3 | position: absolute;
4 | padding-top: 5px;
5 | z-index: 1000;
6 | }
7 | .autocomplete * {
8 | padding: 0;
9 | margin: 0;
10 | }
11 | .autocomplete ul:before {
12 | position: absolute;
13 | top: -5px;
14 | left: 10px;
15 | content: "";
16 | border-left: 5px solid transparent;
17 | border-right: 5px solid transparent;
18 | border-bottom: 5px solid #222;
19 | }
20 | .autocomplete ul {
21 | position: relative;
22 | padding: 4px;
23 | background: #222;
24 | color: #fff;
25 | min-width: 150px;
26 | -webkit-border-radius: 3px;
27 | -moz-border-radius: 3px;
28 | border-radius: 3px;
29 | -webkit-box-shadow: rgba(0,0,0, .1) 0px 3px 5px;
30 | -moz-box-shadow: rgba(0,0,0, .1) 0px 3px 5px;
31 | box-shadow: rgba(0,0,0, .1) 0px 3px 5px;
32 | }
33 | .autocomplete li {
34 | list-style: none;
35 | text-align: left;
36 | padding: 4px 50px 4px 10px;
37 | cursor: pointer;
38 | margin-bottom: 1px;
39 | background: #333;
40 | }
41 | .autocomplete li:last-child {
42 | border-bottom: none;
43 | margin-bottom: 0;
44 | }
45 | .autocomplete li.selected,
46 | .autocomplete li.over {
47 | background: red;
48 | color: #fff;
49 | }
50 |
51 |
52 | /* tagbox styles */
53 | .tagbox {
54 | position: relative;
55 | overflow: hidden;
56 | background: #f9f9f9;
57 | border: 1px solid #ddd;
58 | -webkit-border-radius: 3px;
59 | -moz-border-radius: 3px;
60 | border-radius: 3px;
61 | -webkit-box-shadow: #fff 0px 1px 0px;
62 | -moz-box-shadow: #fff 0px 1px 0px;
63 | box-shadow: #fff 0px 1px 0px;
64 | margin-right: -20px;
65 | }
66 | .tagbox * {
67 | margin: 0;
68 | padding: 0;
69 | }
70 | .tagbox.focus{
71 | outline: 0;
72 | }
73 | .tagbox ul {
74 | overflow: hidden;
75 | padding: 10px 10px 6px 10px;
76 | -webkit-border-radius: 2px;
77 | -moz-border-radius: 2px;
78 | border-radius: 2px;
79 | -webkit-box-shadow: rgba(0,0,0,.07) 0px 2px 4px inset;
80 | -moz-box-shadow: rgba(0,0,0,.07) 0px 2px 4px inset;
81 | box-shadow: rgba(0,0,0,.07) 0px 2px 4px inset;
82 | }
83 | .tagbox li {
84 | position: relative;
85 | list-style: none;
86 | float: left;
87 | cursor: pointer;
88 | margin: 0 4px 4px 0;
89 | }
90 | .tagbox li.new input {
91 | font-family: Arial, sans-serif;
92 | border: none;
93 | display: block;
94 | height: 20px;
95 | line-height: 20px;
96 | font-size: 12px;
97 | background: none;
98 | }
99 | .tagbox li.new input:focus {
100 | outline: none;
101 | }
102 | .tagbox li .tag {
103 | position: relative;
104 | display: block;
105 | background: #222;
106 | color: #fff;
107 | border-radius: 20px;
108 | height: 20px;
109 | line-height: 20px;
110 | font-size-adjust: 100%;
111 | font-size: 12px;
112 | padding: 0 30px 0 12px;
113 | vertical-align: middle;
114 | }
115 | .tagbox li .delete {
116 | position: absolute;
117 | font-family: Arial, sans-serif;
118 | right: 7px;
119 | top: 4px;
120 | color: #fff;
121 | z-index: 100;
122 | cursor: pointer;
123 | color: #222;
124 | background: #fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAMAAACeL25MAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////QkJCfEGH6wAAABRJREFUeNpiYGRgACEgBhMgBBBgAABkAAl7vFkJAAAAAElFTkSuQmCC) no-repeat center center;
125 | width: 12px;
126 | text-indent: -6000px;
127 | font-size: 11px;
128 | line-height: 12px;
129 | height: 12px;
130 | text-align: center;
131 | -webkit-border-radius: 20px;
132 | -moz-border-radius: 20px;
133 | border-radius: 20px;
134 | }
135 | .tagbox .selected .tag {
136 | background: red;
137 | }
--------------------------------------------------------------------------------
/snippets/podcastfeed/podcastfeed.php:
--------------------------------------------------------------------------------
1 | $podcast->title(),
7 | 'link' => url(),
8 | 'language' => 'en-en',
9 | 'modified' => $podcast->modified(),
10 | 'copyright' => $podcast->copyright(),
11 | 'description' => $podcast->description(),
12 | 'subtitle' => $podcast->subtitle(),
13 | 'author' => $podcast->author(),
14 | 'summary' => $podcast->summary(),
15 | 'category' => $podcast->category(),
16 | 'ownername' => $podcast->ownername(),
17 | 'owneremail' => $podcast->owneremail(),
18 | 'items' => array(),
19 | 'image' => ($podcast->images()->first()) ? $podcast->images()->first()->url() : false,
20 | );
21 |
22 | $vars = array_merge($defaults, $vars);
23 | extract($vars);
24 |
25 | // send the right header
26 | header('Content-type: text/xml; charset="utf-8"');
27 |
28 | // echo the doctype
29 | echo '';
30 |
31 | ?>
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 300
61 | 300
62 |
63 |
64 |
65 |
66 |
67 | title()) ?>
68 | url()) ?>
69 | date())) ?>
70 |
71 | text()) ?>]]>
72 |
73 |
74 |
75 |
76 |
77 | images()->first()): ?>
78 |
79 |
80 |
81 | files()->filterBy('extension', 'mp3')->first()): ?>
82 |
83 | url()) ?>
84 |
85 |
86 | duration()) ?>
87 | keywords()) ?>
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/snippets/flattr/readme.mdown:
--------------------------------------------------------------------------------
1 | # Flattr Snippet
2 |
3 | ## What is it?
4 |
5 | This snippet will embed a Flattr button in your site. You can set several options to customize it. The options are nearly equal to the original Flattr parameters. It even has a fallback if javascript is disabled and an optional HTML5 output.
6 |
7 | ## Installation
8 |
9 | Put the flattr.php file in your site/snippets folder
10 |
11 | ## How to use it?
12 |
13 | ```php
14 | 'your_user_id')) ?>
15 | ```
16 |
17 | …or with more options…
18 |
19 | ```php
20 | 'your_user_id',
24 | 'url' => 'http://custom.url/path/to/page',
25 | 'title' => 'Your Page Headline',
26 | 'description' => 'Your Description',
27 | 'category' => 'One of the Flattr Categories',
28 | 'language' => 'en_GB',
29 | 'tags' => 'different, tags',
30 | 'button' => 'compact',
31 | 'popout' => '0',
32 | 'html5' => '1'
33 | ));
34 |
35 | ?>
36 | ```
37 |
38 | ## Options
39 |
40 | ### user_id (required)
41 |
42 | A Flattr username. This is a required parameter for autosubmit but not for things that are already on flattr.com.
43 |
44 | ### url (optional)
45 |
46 | Each thing on Flattr requires a unique URL. All parts of the URL, including the both the parameters and fragments, are used to distinguish the difference between submitted URLs. If this parameter is unset, the snippet grabs the *$page->url()* string.
47 |
48 | ### title (optional)
49 |
50 | Will be used to describe your thing on Fattr. The *title* should be between 5-100 characters. All HTML is stripped. If this parameter is unset, the snippet grabs the *$page->title()* string.
51 |
52 | ### description (optional)
53 |
54 | Will be used to describe your thing. The *description* should be between 5-1000 characters. All HTML is stripped except the * * character which will be converted into newlines *(\n)*.
55 |
56 | ### category (optional)
57 |
58 | This parameter is used to sort things on Flattr and has no impact on the functionality of your button. It's value must be one of the [available categories](https://api.flattr.com/rest/v2/categories.txt).
59 |
60 | ### language (optional)
61 |
62 | Specifies which language your thing is in. Specifying the language will allow visitors on Flattr to filter through the large amount of things and get a personalized feed of items in their selected languages. The value must be a language code from the [available languages](https://api.flattr.com/rest/v2/languages.txt). Note that even tho this parameter is optional, a language will be guessed and added automatically if it is omitted.
63 |
64 | ### tags (optional)
65 |
66 | This parameter allows you to add tags that can be used to describe your thing. This is used on Flattr to allow further filtering and sorting of things. Multiple tags are seperated by a comma *,*. Using non alpha characters in tags are not supported nor is using a comma for obvious reasons.
67 |
68 | ### button (optional)
69 |
70 | We also provide a compact version of our Flattr button. This parameter tells the embedded button script which button layout to use. Set this parameter to *compact* if you want the *compact* button. Don't set the parameter at all if you are fine with the normal button.
71 |
72 | ### popout (optional)
73 |
74 | Set this parameter to *0* to disable the popout that appears when hovering the mouse cursor over the button.
75 |
76 | ### hidden (optional)
77 |
78 | Not all content is suitable for public listing. If you for one reason or another don't want your content to be listed on Flattr set this parameter to *1*.
79 |
80 | ### html5 (optional)
81 |
82 | You can get the output in valid html5 if you set this parameter to *1*.
83 |
84 | Further information can be found on the Flattr developer page:
85 |
86 | # Author
87 | André Dujardin
--------------------------------------------------------------------------------
/plugins/flickrbadge/readme.mdown:
--------------------------------------------------------------------------------
1 | # Flickrbadge Plugin
2 |
3 | ## What is it?
4 |
5 | It's a little plugin to display the latest public pictures from your Flickr account.
6 |
7 | ## Installation
8 |
9 | Copy the flickrbadge.php file to your site/plugins folder.
10 | You should make sure to enable caching. Otherwise the badge will make multiple API calls to Flickr's API every time the page is refreshed, which takes very long.
11 |
12 | - Go to site/config/config.php to set c::set('cache', true);
13 | - Make sure that site/cache is writable (change permissions to 0755)
14 |
15 | ## How to use it?
16 |
17 | You can add it to your templates like this:
18 |
19 | 'your-flickr-api-key',
23 | 'username' => 'your-flickr-username',
24 | 'limit' => 4,
25 | 'format' => 'medium'
26 | ));
27 |
28 | ?>
29 |
30 |
31 |
32 |
33 |
title() ?>
34 |
taken()) ?>
35 |
Views: views() ?>
36 |
Comments: comments() ?>
37 |
Tags: tags()) ?>
38 |
39 |
40 |
41 |
42 |
43 | ## Available Options
44 |
45 | ### key (required)
46 |
47 | Set your Flickr api key. You can get a Flickr API Key over here:
48 |
49 | ### username (required)
50 |
51 | Set your Flickr username.
52 |
53 | ### limit (default: 10)
54 |
55 | Set the number of images you'd like to display in your badge
56 |
57 | ### format (default: square)
58 |
59 | Set the image format for your badge. The following formats are available:
60 |
61 | - square
62 | - thumbnail
63 | - small
64 | - medium
65 | - medium_640
66 | - large
67 | - original
68 |
69 | ### cache (default: true)
70 |
71 | Disable or enable the cache. Caching the results is highly recommended. Otherwise the badge will be super slow. You must make sure that `site/cache` is writable (change permissions to 0755) You must also set c::set('cache', true); in site/config/config.php to enable the cache.
72 |
73 | ### refresh (default: two hours)
74 |
75 | Set the cache expiry in seconds. By default it will be set to two hours. After two hours the badge will try to fetch fresh data from the API. Photo and user information will be kept forever unless you delete the cache files manually. This will speed up the update process.
76 |
77 | ## Available variables for $picture objects
78 |
79 | When looping through the result array you can use the following variables:
80 |
81 | ### $picture->url()
82 |
83 | The url to the Flickr picture page
84 |
85 | ### $picture->src()
86 |
87 | The url of the image file
88 |
89 | ### $picture->title()
90 |
91 | The title of the picture
92 |
93 | ### $picture->description()
94 |
95 | The description for the picture
96 |
97 | ### $picture->tags()
98 |
99 | An array of tags
100 |
101 | ### $picture->taken()
102 |
103 | The unix timestamp when the picture has been taken
104 |
105 | ### $picture->posted()
106 |
107 | The unix timestamp when the picture has been posted on Flickr
108 |
109 | ### $picture->lastupdate()
110 |
111 | The unix timestamp when the picture has been updated
112 |
113 | ### $picture->views()
114 |
115 | The number of views for this picture
116 |
117 | ### $picture->comments()
118 |
119 | The number of comments for this picture
120 |
121 |
122 | ## Credits
123 |
124 | The plugin uses the awesome phpFlickr Class by Dan Coulter: http://phpflickr.com/
125 |
126 | ## Author
127 | Bastian Allgeier
128 |
--------------------------------------------------------------------------------
/plugins/dribbble/readme.mdown:
--------------------------------------------------------------------------------
1 | # Dribbble Plugin for Kirby by Simon Albrecht (1.1)
2 | This is a plugin for [Kirby](http://getkirby.com/) that fetches shots, player-info or likes for a user on [Dribbble](http://dribbble.com/) from the [Dribbble](http://dribbble.com/)-API.
3 |
4 | ## Installation
5 | Put the `dribbble.php` file in your `site/plugins` folder. If this folder doesn't exist yet, create it.
6 |
7 | ## Update instructions
8 | To update, just replace the old `dribbble.php` file in `site/plugins`, with the new one.
9 |
10 | ## Limitation
11 | The use of the [Dribbble API](http://dribbble.com/api) is restricted to a maximum of 60 API calls per minute.
12 | If your site exceeds that maximum, think about [enabling the cache](http://getkirby.com/docs/advanced-stuff/caching) for the site.
13 |
14 | ## Example usage
15 | shots();
20 | $player = $dribbble->player();
21 | $likes = $dribbble->likes();
22 |
23 | foreach ($shots as $shot): ?>
24 |
25 |
46 |
47 | An example for use of this plugin can be viewed [on my website](http://albrecht.me/#dribbble).
48 |
49 | ## Attributes for the shot-object
50 | * `$shot->id` The ID of the shot
51 | * `$shot->title` The title of the shot
52 | * `$shot->url` The (long) url of the shot
53 | * `$shot->short_url` The short-url of the shot
54 | * `$shot->image` The image-url of the shot
55 | * `$shot->likes` The number of likes on the shot
56 | * `$shot->views` The number of views on the shot
57 | * `$shot->rebounds` The number of rebounds of the shot
58 | * `$shot->comments` The number of comments on the shot
59 | * `$shot->created` The date/time the liked post was created
60 |
61 | ## Attributes for the player-object
62 | * `$player->id` The ID of the player
63 | * `$player->name` The name of the player
64 | * `$player->username` The username of the player
65 | * `$player->avatar_url` The avatar-url of the player
66 | * `$player->twitter` The Twitter handle of the player
67 | * `$player->location` The location of the player
68 | * `$player->followers` The number of followers of the player
69 | * `$player->following` The number of users the player is following
70 | * `$player->likes` The number of shots the player has liked
71 |
72 | ## Attributes for the like-object
73 | * `$like->id` The ID of the liked shot
74 | * `$like->title` The title of the liked shot
75 | * `$like->url` The (long) url of the liked shot
76 | * `$like->short_url` The short-url of the liked shot
77 | * `$like->image` The image-url of the liked shot
78 | * `$like->likes` The number of likes on the liked shot
79 | * `$like->views` The number of views on the liked shot
80 | * `$like->rebounds` The number of rebounds of liked the shot
81 | * `$like->comments` The number of comments on liked the shot
82 | * `$like->created` The date/time the liked post was created
83 | * `$like->player` A representation of the user that created the liked shot. (same attributes as the player-object)
84 |
85 | ## Requirements
86 | Your web server must have support for cURL [(installation instructions)](http://www.php.net/manual/en/curl.installation.php).
87 |
88 | ## Author & Copyright
89 | Copyright 2012, Simon Albrecht [http://albrecht.me/](http://albrecht.me).
90 | If you use this plugin, feel free to ping me [@s_albrecht](http://twitter.com/s_albrecht).
--------------------------------------------------------------------------------
/plugins/auth/readme.mdown:
--------------------------------------------------------------------------------
1 | # Authentication Plugin
2 |
3 | ## What is it?
4 |
5 | The Authentication plugin makes it possible to create user accounts and user groups for your website and restrict access to certain parts.
6 |
7 | ## Installation
8 |
9 | - Copy the `auth` folder to your `site/plugins` folder.
10 | - Copy `auth/snippets/login.php` to your `snippets` folder and optionally customize it
11 | - Create an invisible `content/login` folder with a `login.txt`
12 | - Create a `login.php` template in `site/templates` and include the login form snippet. I.e.:
13 |
14 | ```
15 |
16 |
17 |
18 |
19 |
20 | ```
21 |
22 | - Create an invisible `content/logout` folder with a `logout.txt`
23 | - Crate a `logout.php` template in `site/templates`:
24 |
25 | ```
26 |
27 | ```
28 |
29 | - Create an `site/accounts` folder and add your first user account. Use the username as filename (ie. `yourusername.php`).
30 |
31 | ```
32 |
33 |
34 | # setup your account credentials here
35 | # it's highly recommended to use md5 or sha1 encryption
36 | # for your passwords. read more about encryption in the
37 | # docs: http://getkirby.com/docs/panel/accounts
38 |
39 | username: yourusername
40 | password: yourpassword
41 | group: optional-group-name
42 | ```
43 |
44 | Now you have a fully functional login/logout system.
45 |
46 | ## Setting up the firewall
47 |
48 | ### Full restriction
49 |
50 | If you want to restrict access to all your pages, I recommend to add the firewall to your `header.php` snippet:
51 |
52 | ```
53 |
54 | ```
55 |
56 | This will block access to all pages for all unauthenticated users.
57 |
58 |
59 | ### Restriction per template
60 |
61 | If you want to fine-tune access to certain templates you should add the firewall as the first line of each template, which should have restricted access.
62 |
63 | ### General blocking + fine-tuning
64 |
65 | As a third alternative you can setup a general firewall in the header.php snippet and an additional firewall for single templates. This will give you full control over who can access what.
66 |
67 | ## Customizing the firewall
68 |
69 | The firewall has a bunch of settings, which can be used to give access to certain users or groups
70 |
71 | ### redirect (default: login)
72 |
73 | By default the firewall is redirecting to the login page, but you can change the redirect url with this option:
74 |
75 | // This firewall will redirect to the homepage
76 | Auth::firewall(array(
77 | 'redirect' => '/'
78 | ));
79 |
80 | // This firewall will redirect to the error page
81 | Auth::firewall(array(
82 | 'redirect' => 'error'
83 | ));
84 |
85 | ### logout (default: true)
86 |
87 | By default the firewall will logout users which are not allowed to access the current page. You can change this here:
88 |
89 | // This firewall will just redirect but not logout
90 | Auth::firewall(array(
91 | 'logout' => false
92 | ));
93 |
94 |
95 | ### allow
96 |
97 | By default the firewall will allow access to all available users and groups. You can fine-tune this here:
98 |
99 | // This firewall will only grant access to the user "yourusername"
100 | Auth::firewall(array(
101 | 'allow' => array('user:yourusername')
102 | ));
103 |
104 | // This firewall will only grant access to users in the group "admin"
105 | Auth::firewall(array(
106 | 'allow' => array('group:admin')
107 | ));
108 |
109 | // This firewall will only grant access to users in the group "admin" and a user "bart"
110 | Auth::firewall(array(
111 | 'allow' => array('group:admin', 'user:bart')
112 | ));
113 |
114 | ### deny
115 |
116 | You can also deny certain users or groups
117 |
118 | Auth::firewall(array(
119 | 'deny' => array('user:yourusername', 'group:editors')
120 | ));
121 |
122 | Deny will overwrite allow rules.
123 |
124 | ## The login snippet
125 |
126 | The login snippet should be customized to fit your needs for the login form.
127 | The Auth::login() method will always be needed in the first line to make the form work.
128 | You can set the redirect URL, which will be used to redirect users after a successful login.
129 | By default users will be taken to the homepage:
130 |
131 |
132 |
133 | ## The logout template
134 |
135 | In the logout template you are also able to specify the redirect URL after logouts like this:
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/plugins/tweets/tweets.php:
--------------------------------------------------------------------------------
1 | 10,
7 | 'cache' => true,
8 | 'hiderep' => false, //Parameter to hide replies in your feed - for accounts with high reply volumes
9 | 'refresh' => 60*20 // refresh every 20 minutes
10 | );
11 |
12 | // add the username to the defaults array
13 | $defaults['username'] = $username;
14 |
15 | $options = array_merge($defaults, $params);
16 |
17 | // check the cache dir
18 | $cacheDir = c::get('root.cache') . '/tweets';
19 | dir::make($cacheDir);
20 |
21 | // disable the cache if adding the cache dir failed
22 | if(!is_dir($cacheDir) || !is_writable($cacheDir)) $options['cache'] = false;
23 |
24 | // sanitize the limit
25 | if($options['limit'] > 200) $options['limit'] = 200;
26 |
27 | // generate a unique cache ID
28 | $cacheID = 'tweets/tweets.' . md5($options['username']) . '.' . $options['limit'] . '.php';
29 |
30 | if($options['cache']) {
31 | $cache = (cache::modified($cacheID) < time()-$options['refresh']) ? false : cache::get($cacheID);
32 | } else {
33 | $cache = false;
34 | }
35 |
36 | if(!empty($cache)) return $cache;
37 |
38 | // Encode the key and secret from the Twitter config.
39 | $twitterKey = urlencode(c::get('twitter.key'));
40 | $twitterSecret = urlencode(c::get('twitter.secret'));
41 |
42 | // combine and base64 encode the key and secret with a colon seperator
43 | $twitterCode = base64_encode( $twitterKey . ':' . $twitterSecret );
44 |
45 | // obtain a bearer token from the api, by building a request
46 |
47 | //url to use
48 | $url = 'https://api.twitter.com/oauth2/token';
49 |
50 | //create header
51 | $header = array(
52 | 'http' => array(
53 | 'method' => "POST",
54 | 'header' => "Content-type: application/x-www-form-urlencoded;charset=UTF-8\r\n"
55 | ."Authorization: Basic " . $twitterCode ."\r\n",
56 | 'content' => "grant_type=client_credentials",
57 | ),
58 | );
59 |
60 | //send the request
61 | $context = stream_context_create($header);
62 | $bearer = file_get_contents($url, false, $context);
63 |
64 | // decode the json response
65 | $bearer = json_decode($bearer);
66 |
67 | // send the rquest for tweets
68 |
69 | $url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=' . $options['username'] . '&count=' . $options['limit']. '&include_rts=true' . '&exclude_replies=' . $options['hiderep'];
70 |
71 | $header = array(
72 | 'http' => array(
73 | 'method' => "GET",
74 | 'header' => "Authorization: Bearer " . $bearer->access_token ."\r\n",
75 | ),
76 | );
77 |
78 | $context = stream_context_create($header);
79 |
80 | $json = file_get_contents($url, false, $context);
81 |
82 | $data = json_decode($json);
83 |
84 | if(!$data) return false;
85 |
86 | $result = array();
87 |
88 | foreach($data as $tweet) {
89 |
90 | $user = $tweet->user;
91 |
92 | $result[] = new tweet(array(
93 | 'url' => 'http://twitter.com/' . $options['username'] . '/status/' . $tweet->id_str,
94 | 'text' => $tweet->text,
95 | 'date' => strtotime($tweet->created_at),
96 | 'source' => $tweet->source,
97 | 'user' => new obj(array(
98 | 'name' => $user->name,
99 | 'bio' => $user->description,
100 | 'username' => $user->screen_name,
101 | 'url' => 'http://twitter.com/' . $user->screen_name,
102 | 'image' => 'http://twitter.com/api/users/profile_image/' . $user->screen_name,
103 | 'following' => $user->friends_count,
104 | 'followers' => $user->followers_count,
105 | ))
106 | ));
107 |
108 | }
109 |
110 | $result = new obj($result);
111 |
112 | if($options['cache']) cache::set($cacheID, $result);
113 |
114 | return $result;
115 |
116 | }
117 |
118 | class tweet extends obj {
119 |
120 | function date($format=false) {
121 | return ($format) ? date($format, $this->date) : $this->date;
122 | }
123 |
124 | function text($link=false) {
125 | return ($link) ? self::link(html($this->text)) : $this->text;
126 | }
127 |
128 | static function link($text) {
129 | $text = preg_replace('/(http|https):\/\/([a-z0-9_\.\-\+\&\!\#\~\/\,]+)/i', '$1://$2', $text);
130 | $text = preg_replace('/@([A-Za-z0-9_]+)/is', '@$1', $text);
131 | $text = preg_replace('/#([A-Aa-z0-9_-]+)/is', '#$1', $text);
132 | return $text;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/plugins/email/readme.mdown:
--------------------------------------------------------------------------------
1 | # Email Plugin
2 |
3 | ## What is it?
4 |
5 | It's a simple email sender plugin. You can use it everywhere in your code to send emails via PHP's mail(), [Amazon Simple Email Service](http://aws.amazon.com/ses/) or [Postmark](http://postmarkapp.com/)
6 |
7 | ## Installation
8 |
9 | Copy the email.php file to your site/plugins folder.
10 |
11 | ### Amazon SES configuration
12 |
13 | Go to your site/config/config.php and add the following rules:
14 |
15 | c::set('email.use', 'amazon');
16 | c::set('email.amazon.key', 'Your Amazon SES Key');
17 | c::set('email.amazon.secret', 'Your Amazon SES Secret');
18 |
19 | ### Postmark configuration
20 |
21 | Go to your site/config/config.php and add the following rules:
22 |
23 | c::set('email.use', 'postmark');
24 | c::set('email.postmark.key', 'Your Postmark API Key');
25 |
26 | ## How to use it?
27 |
28 | You can use it in your templates, snippets or plugins like this:
29 |
30 | 'John Doe ',
34 | 'from' => 'Max Musterman ',
35 | 'subject' => 'My first Email',
36 | 'body' => 'Hello my friend! How are things going?'
37 | ));
38 |
39 | ?>
40 |
41 | ### React on sending errors
42 |
43 | 'John Doe ',
47 | 'from' => 'Max Musterman ',
48 | 'subject' => 'My first Email',
49 | 'body' => 'Hello my friend! How are things going?'
50 | ));
51 |
52 | if(error($send)) {
53 |
54 | // show the response
55 | a::show($send);
56 |
57 | // or do something else like saying that the email failed
58 | echo "The email could not be sent";
59 |
60 | }
61 |
62 | ?>
63 |
64 | ## Available Params
65 |
66 | This is the full list of params you can pass to the email function:
67 |
68 | ### to
69 |
70 | The address the email will be sent to. You can use either just the address…
71 |
72 | 'to' => 'john@doe.com'
73 |
74 | …or the address together with a name…
75 |
76 | 'to' => 'John Doe '
77 |
78 | If you are always sending to the same address, you can setup the to address in your config like this:
79 |
80 | c::set('email.to', 'John Doe ');
81 |
82 | Afterwards you don't need to pass the to address when calling the email function.
83 |
84 | ### from
85 |
86 | The address from which the mail has been sent. Please make sure to use an address which you are allowed to use to avoid spam. The following formats are allowed
87 |
88 | 'from' => 'john@doe.com'
89 |
90 | 'from' => 'John Doe '
91 |
92 | If you are always sending from the same address, you can setup the from address in your config like this:
93 |
94 | c::set('email.from', 'John Doe ');
95 |
96 | Afterwards you don't need to pass the to address when calling the email function.
97 |
98 | ### replyto
99 |
100 | The reply address. The from address will be used if you don't pass this. The following formats are allowed
101 |
102 | 'from' => 'john@doe.com'
103 |
104 | 'from' => 'John Doe '
105 |
106 | If you are always using the same reply address, you can setup the address in your config like this:
107 |
108 | c::set('email.replyto', 'John Doe ');
109 |
110 | Afterwards you don't need to pass the to address when calling the email function.
111 |
112 | ### subject
113 |
114 | The subject line for your email. If you are always using the same subject you can setup a default subject in your config:
115 |
116 | c::set('email.subject', 'This is my default subject');
117 |
118 | ### body
119 |
120 | The plain text for your email. The email plugin does not support sending HTML emails.
121 |
122 | ### use
123 |
124 | If you want to switch the transport service, you can do it like this:
125 |
126 | 'use' => 'mail'
127 |
128 | 'use' => 'amazon'
129 |
130 | 'use' => 'postmark'
131 |
132 | ### postmark.key
133 |
134 | If you want to send from a different postmark api key, you can pass the key in the params, when calling the email function.
135 |
136 | ### postmark.test
137 |
138 | You can pass `postmark.test => true` when calling the email function to make a test call instead of sending a real request to the postmark api. Unfortunately this is not supported by the Amazon API as well.
139 |
140 | ### amazon.key
141 |
142 | If you want to send from a different amazon api key, you can pass the key in the params, when calling the email function.
143 |
144 | ### amazon.secret
145 |
146 | If you want to send from a different amazon api secret, you can pass the key in the params, when calling the email function.
147 |
148 |
149 | ## Author
150 | Bastian Allgeier
151 |
--------------------------------------------------------------------------------
/plugins/highlight/highlight/geshi/js.php:
--------------------------------------------------------------------------------
1 | 'Javascript',
44 | 'COMMENT_SINGLE' => array(1 => '//'),
45 | 'COMMENT_MULTI' => array('/*' => '*/'),
46 | 'CASE_KEYWORDS' => GESHI_CAPS_NO_CHANGE,
47 | 'QUOTEMARKS' => array("'", '"'),
48 | 'ESCAPE_CHAR' => '\\',
49 | 'KEYWORDS' => array(
50 | 1 => array(
51 | 'as', 'break', 'case', 'catch', 'continue', 'decodeURI', 'delete', 'do',
52 | 'else', 'encodeURI', 'eval', 'finally', 'for', 'if', 'in', 'is', 'item',
53 | 'instanceof', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'void',
54 | 'while', 'write', 'with'
55 | ),
56 | 2 => array(
57 | 'class', 'const', 'default', 'debugger', 'export', 'extends',
58 | 'function', 'import', 'namespace', 'new', 'null', 'package', 'private',
59 | 'protected', 'public', 'super', 'use', 'var'
60 | ),
61 | 3 => array(
62 |
63 | // common functions for Window object
64 | 'alert', 'back', 'blur', 'close', 'confirm', 'focus', 'forward', 'home',
65 | 'name', 'navigate', 'onblur', 'onerror', 'onfocus', 'onload', 'onmove',
66 | 'onresize', 'onunload', 'open', 'print', 'prompt', 'scroll', 'status',
67 | 'stop',
68 | ),
69 | 4 => array(
70 | 'false', 'true'
71 | )
72 | ),
73 | 'SYMBOLS' => array(
74 | '(', ')', '[', ']', '{', '}', '!', '@', '%', '&', '*', '|', '/', '<', '>'
75 | ),
76 | 'CASE_SENSITIVE' => array(
77 | GESHI_COMMENTS => false,
78 | 1 => false,
79 | 2 => false,
80 | 3 => false
81 | ),
82 | 'STYLES' => array(
83 | 'KEYWORDS' => array(
84 | 1 => 'color: #000066; font-weight: bold;',
85 | 2 => 'color: #003366; font-weight: bold;',
86 | 3 => 'color: #000066;'
87 | ),
88 | 'COMMENTS' => array(
89 | 1 => 'color: #009900; font-style: italic;',
90 | 'MULTI' => 'color: #009900; font-style: italic;'
91 | ),
92 | 'ESCAPE_CHAR' => array(
93 | 0 => 'color: #000099; font-weight: bold;'
94 | ),
95 | 'BRACKETS' => array(
96 | 0 => 'color: #66cc66;'
97 | ),
98 | 'STRINGS' => array(
99 | 0 => 'color: #3366CC;'
100 | ),
101 | 'NUMBERS' => array(
102 | 0 => 'color: #CC0000;'
103 | ),
104 | 'METHODS' => array(
105 | 1 => 'color: #006600;'
106 | ),
107 | 'SYMBOLS' => array(
108 | 0 => 'color: #66cc66;'
109 | ),
110 | 'REGEXPS' => array(
111 | 0 => 'color: #0066FF;'
112 | ),
113 | 'SCRIPT' => array(
114 | 0 => '',
115 | 1 => '',
116 | 2 => '',
117 | 3 => ''
118 | )
119 | ),
120 | 'URLS' => array(
121 | 1 => '',
122 | 2 => '',
123 | 3 => ''
124 | ),
125 | 'OOLANG' => true,
126 | 'OBJECT_SPLITTERS' => array(
127 | 1 => '.'
128 | ),
129 | 'REGEXPS' => array(
130 | 0 => "/.*/([igm]*)?" // matches js reg exps
131 | ),
132 | 'STRICT_MODE_APPLIES' => GESHI_MAYBE,
133 | 'SCRIPT_DELIMITERS' => array(
134 | 0 => array(
135 | ''
136 | ),
137 | 1 => array(
138 | ''
139 | )
140 | ),
141 | 'HIGHLIGHT_STRICT_BLOCK' => array(
142 | 0 => true,
143 | 1 => true
144 | )
145 | );
146 |
147 | ?>
148 |
--------------------------------------------------------------------------------
/exporters/posterous/posterous.php:
--------------------------------------------------------------------------------
1 | "Users" > "api_token"
43 | $token = 'yourtoken';
44 |
45 | // setup the desired name for your content files
46 | $template = 'article.txt';
47 |
48 | // setup the date format, which will
49 | // be used in your content files
50 | // default is 2012-02-09 16:00
51 | $dateformat = 'Y-m-d H:i';
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | // DON'T TOUCH THE LINES BELOW
66 | // ===========================
67 |
68 | // Stuff to run it with kpm
69 | if(isset($kpm)) {
70 | @extract($kpm['options']);
71 | $root = $kpm['contentdir'];
72 |
73 | function puterror($error) {
74 | kpmerror($error);
75 | }
76 |
77 | function putmessage($message) {
78 | kpmlog($message);
79 | }
80 | } else {
81 | require('kirby/lib/kirby.php');
82 | function puterror($error) {
83 | dir($error);
84 | }
85 |
86 | function putmessage($message) {
87 | echo $message;
88 | }
89 | }
90 |
91 | set_time_limit(0);
92 |
93 | if($username == 'your@email-address.com' && $password == 'yourpassword' && $blog == 'yourblog') {
94 | puterror('Please setup the credentials for your Posterous blog. Open posterous.php in your favorite editor and follow the instructions.');
95 | }
96 |
97 | if(!is_dir($root)) puterror('The blog directory does not exist');
98 | if(!is_writable($root)) puterror('The blog directory is not writable');
99 |
100 | $posts = array();
101 | $page = 1;
102 |
103 | $p = new posterous($username, $password, $token);
104 |
105 | while($data = $p->call('http://posterous.com/api/2/sites/' . $blog . '/posts/public', array('page' => $page))) {
106 |
107 | foreach($data as $d) {
108 | $posts[] = $d;
109 | }
110 |
111 | sleep(1);
112 |
113 | $page++;
114 |
115 | }
116 |
117 | if(!$posts) puterror('The posts couldn\'t be found');
118 |
119 | $cnt = count($posts);
120 | $len = str::length($cnt);
121 |
122 | if($len <= 1) $len = 2;
123 |
124 | $n = 0;
125 | $skipped = array();
126 | $errors = array();
127 |
128 | foreach(array_reverse($posts) as $post) {
129 |
130 | $n++;
131 | $output = array();
132 |
133 | if(empty($post->title) || empty($post->slug)) {
134 | $errors[] = $post;
135 | continue;
136 | }
137 |
138 | // collect tags
139 | $tags = array();
140 | foreach($post->tags as $t) $tags[] = $t->name;
141 |
142 |
143 | $output[] = 'title: ' . $post->title;
144 | $output[] = 'date: ' . date($dateformat, strtotime($post->display_date));
145 | $output[] = 'text: ' . "\n\n" . trim($post->body_full);
146 | $output[] = 'tags: ' . implode(', ', $tags);
147 |
148 | $name = pad($n, $len) . '-' . f::safe_name(basename($post->slug));
149 | $dir = $root . '/' . $name;
150 |
151 | if(is_dir($dir)) {
152 | $skipped[] = basename($dir);
153 | continue;
154 | }
155 |
156 | dir::make($dir);
157 |
158 | $content = implode("\n\n" . '----' . "\n\n", $output);
159 | $file = $dir . '/' . $template;
160 |
161 | f::write($file, $content);
162 |
163 | }
164 |
165 | putmessage('Exported ' . $n . ' articles to ' . $root . '
');
166 |
167 | if(!empty($errors)) {
168 | putmessage(count($errors) . ' article(s) could not be imported
');
169 | }
170 |
171 | if(!empty($skipped)) {
172 | putmessage('The following folders have been skipped, because they already existed:' . a::show($skipped, false));
173 | }
174 |
175 | // padding zero function
176 | function pad($number,$n) {
177 | return str_pad((int) $number,$n,"0",STR_PAD_LEFT);
178 | }
179 |
180 | // Minimal Posterous API Class
181 | class posterous {
182 |
183 | function __construct($username, $password, $token) {
184 |
185 | $this->username = $username;
186 | $this->password = $password;
187 | $this->token = $token;
188 |
189 | }
190 |
191 | function call($url, $args = array()) {
192 |
193 | $args['api_token'] = $this->token;
194 |
195 | $url = $url . '?' . http_build_query($args);
196 |
197 | $ch = curl_init();
198 | curl_setopt($ch, CURLOPT_URL, $url);
199 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
200 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
201 | curl_setopt($ch, CURLOPT_HEADER, false);
202 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
203 | curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
204 | curl_setopt($ch, CURLOPT_POST, 0);
205 |
206 | $data = curl_exec($ch);
207 | curl_close($ch);
208 |
209 | return @json_decode($data);
210 |
211 | }
212 |
213 | }
214 |
--------------------------------------------------------------------------------
/plugins/search/search.php:
--------------------------------------------------------------------------------
1 | fields = a::get($options, 'fields', array());
54 | $this->score = a::get($options, 'score', array());
55 | $this->words = a::get($options, 'words');
56 | $this->ignore = a::get($options, 'ignore', array());
57 | $this->include = a::get($options, 'include', 'all');
58 | $this->in = a::get($options, 'in');
59 | $this->minlength = a::get($options, 'minlength', false);
60 | $this->stopwords = a::get($options, 'stopwords', array());
61 | $this->query = a::get($options, 'query', false);
62 | $this->searchfield = a::get($options, 'searchfield', false);
63 | $this->paginate = a::get($options, 'paginate', false);
64 | $this->mode = str::lower(a::get($options, 'mode')) == 'and' ? 'and' : 'or';
65 |
66 | $result = array();
67 |
68 | // if you set a searchfield instead of a query
69 | // the query will automatically be fetched from
70 | // post or get requests
71 | if($this->searchfield) {
72 | $this->query = trim(urldecode(get($this->searchfield)));
73 | }
74 |
75 | // stop here if no searchword is found
76 | if(empty($this->query)) return false;
77 |
78 | $this->searchwords = preg_replace('/[^\pL]/u',',', preg_quote($this->query));
79 | $this->searchwords = str::split($this->searchwords, ',', $this->minlength);
80 |
81 | if(!empty($this->stopwords)) {
82 | $this->searchwords = array_diff($this->searchwords, $this->stopwords);
83 | }
84 |
85 | if(empty($this->searchwords)) return false;
86 |
87 | // do this to save the count function for all loops
88 | $countSearchwords = count($this->searchwords);
89 |
90 | // define the set of pages to search in
91 | $pages = ($this->in) ? $site->pages()->find($this->in)->children()->index() : $site->pages()->index();
92 |
93 | foreach($pages as $page) {
94 |
95 | if($this->include == 'visible' && !$page->isVisible()) continue;
96 | if($this->include == 'invisible' && $page->isVisible()) continue;
97 |
98 | if(in_array($page->uri(), $this->ignore)) continue;
99 |
100 | if(!empty($this->fields)) {
101 | $keys = array_intersect(array_keys($page->content->variables), $this->fields);
102 | } else if($page->content) {
103 | $keys = array_keys($page->content->variables);
104 | }
105 |
106 | $found = array();
107 | $matchedTotal = 0;
108 | $score = 0;
109 |
110 | foreach($keys as $field) {
111 | $value = $page->$field;
112 |
113 | $matchedPerField = 0;
114 | $matchedWords = 0;
115 | $fieldScore = a::get($this->score, $field, 1);
116 |
117 | foreach($this->searchwords as $s) {
118 |
119 | // only match words
120 | if($this->words) {
121 | $m = @preg_match_all('!\b' . $s . '\b!i', $value, $array);
122 | } else {
123 | $m = @preg_match_all('!' . $s . '!i', $value, $array);
124 | }
125 |
126 | // track matched search words
127 | if($m) $matchedWords++;
128 |
129 | // add the matches to the page
130 | $matchedPerField = $matchedPerField+$m;
131 |
132 | }
133 |
134 | if(
135 | $this->mode == 'and' && $countSearchwords == $matchedWords && $matchedPerField > 0 ||
136 | $this->mode == 'or' && $matchedPerField > 0
137 | ) {
138 | // add the number of hits;
139 | $matchedTotal = $matchedTotal+$matchedPerField;
140 |
141 | // apply the score for this field
142 | $score = $score+($matchedPerField*$fieldScore);
143 | }
144 |
145 | }
146 |
147 | // add all matched pages to the result set
148 | if($matchedTotal) {
149 | $result[$page->uid] = $page;
150 | $result[$page->uid]->searchHits = $matchedTotal;
151 | $result[$page->uid]->searchScore = $score;
152 | }
153 |
154 | }
155 |
156 | if(empty($result)) return false;
157 |
158 | $pages = new pages($result);
159 | $pages = $pages->sortBy('searchScore','desc');
160 |
161 | // add pagination
162 | if($this->paginate) $pages = $pages->paginate($this->paginate, array('mode' => 'query'));
163 |
164 | $this->results = $pages;
165 |
166 | }
167 |
168 | function results() {
169 | return $this->results;
170 | }
171 |
172 | function query() {
173 | return $this->query;
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/plugins/auth/auth.php:
--------------------------------------------------------------------------------
1 | 'error',
25 | 'msg' => l::get('auth.error', 'Invalid username or password')
26 | );
27 |
28 | // check for matching usernames
29 | if(str::lower($account->username()) != str::lower($username)) return array(
30 | 'status' => 'error',
31 | 'msg' => l::get('auth.error', 'Invalid username or password')
32 | );
33 |
34 | // check for a matching password
35 | if(!self::checkPassword($account, $password)) return array(
36 | 'status' => 'error',
37 | 'msg' => l::get('auth.error', 'Invalid username or password')
38 | );
39 |
40 | // generate a random token
41 | $token = str::random();
42 |
43 | // add the username.
44 | $account->token = $token;
45 |
46 | // store the token in the cookie
47 | // and the user data in the session
48 | cookie::set('authFrontend', $token, 60*60*24);
49 | s::set('authFrontend.' . $token, $account->username());
50 |
51 | go(url($redirect));
52 |
53 | }
54 |
55 | static function user() {
56 |
57 | if(!is_null(self::$user)) return self::$user;
58 |
59 | $token = cookie::get('authFrontend');
60 |
61 | if(empty($token)) return self::$user = false;
62 |
63 | $username = s::get('authFrontend.' . $token, false);
64 |
65 | if(empty($username)) return self::$user = false;
66 |
67 | $account = self::load($username);
68 |
69 | // make sure to remove the password
70 | // because this should never be visible to anybody
71 | unset($account->_['password']);
72 |
73 | if(empty($account) || $account->username() != $username) return self::$user = false;
74 |
75 | $account->token = $token;
76 | return self::$user = $account;
77 |
78 | }
79 |
80 | static protected function kill() {
81 |
82 | self::$user = null;
83 |
84 | // overwrite the token
85 | $token = str::random();
86 | // the cookie is valid for 24 hours
87 | cookie::set('authFrontend', $token, 60*60*24);
88 |
89 | // restart the session
90 | s::restart();
91 |
92 | }
93 |
94 | static public function logout($redirect = 'login') {
95 |
96 | self::kill();
97 |
98 | // go to the homepage
99 | go(url($redirect));
100 |
101 | }
102 |
103 | static public function firewall($params = array()) {
104 |
105 | global $site;
106 |
107 | $defaults = array(
108 | 'ignore' => array('login', 'logout'),
109 | 'redirect' => 'login',
110 | 'allow' => array(),
111 | 'deny' => array(),
112 | 'logout' => true,
113 | );
114 |
115 | $options = array_merge($defaults, $params);
116 | $page = $site->pages()->active();
117 |
118 | if(in_array($page->uid(), $options['ignore'])) return true;
119 |
120 | // get the current user
121 | $user = self::user();
122 |
123 | if(!$user) {
124 | if($options['logout']) self::kill();
125 | go(url($options['redirect']));
126 | }
127 |
128 | $allowed = false;
129 |
130 | if(is_string($options['allow'])) $options['allow'] = array($options['allow']);
131 | if(is_string($options['deny'])) $options['deny'] = array($options['deny']);
132 |
133 | if(empty($options['allow'])) {
134 | $allowed = true;
135 | } else {
136 |
137 | foreach($options['allow'] as $allow) {
138 |
139 | // user
140 | if(preg_match('!^user:!', $allow)) {
141 | $username = str_replace('user:', '', $allow);
142 |
143 | if(str::lower($username) == str::lower($user->username())) {
144 | $allowed = true;
145 | break;
146 | }
147 |
148 | } else if(preg_match('!^group:!', $allow)) {
149 | $group = str_replace('group:', '', $allow);
150 |
151 | if($user->group() != '' && str::lower($group) == str::lower($user->group())) {
152 | $allowed = true;
153 | break;
154 | }
155 |
156 | }
157 |
158 | }
159 |
160 | }
161 |
162 | // deny loop
163 |
164 | foreach($options['deny'] as $allow) {
165 |
166 | // user
167 | if(preg_match('!^user:!', $allow)) {
168 | $username = str_replace('user:', '', $allow);
169 |
170 | if(str::lower($username) == str::lower($user->username())) {
171 | $allowed = false;
172 | break;
173 | }
174 |
175 | } else if(preg_match('!^group:!', $allow)) {
176 | $group = str_replace('group:', '', $allow);
177 |
178 | if($user->group() != '' && str::lower($group) == str::lower($user->group())) {
179 | $allowed = false;
180 | break;
181 | }
182 |
183 | }
184 |
185 | }
186 |
187 |
188 | if(!$allowed) {
189 | if($options['logout']) self::kill();
190 | go(url($options['redirect']));
191 | }
192 |
193 | return true;
194 |
195 | }
196 |
197 | static protected function load($username) {
198 |
199 | $username = str::lower($username);
200 |
201 | $dir = c::get('root.site') . '/accounts';
202 | $file = $dir . '/' . $username . '.php';
203 |
204 | if(!is_dir($dir) || !file_exists($file)) return false;
205 |
206 | $content = file_get_contents($file);
207 | $yaml = yaml($content);
208 |
209 | // remove the php direct access protection line
210 | unset($yaml[0]);
211 |
212 | return new AuthUser($yaml);
213 |
214 | }
215 |
216 | static protected function checkPassword($user, $password) {
217 |
218 | // check for empty passwords
219 | if(empty($password) || $user->password() == '') return false;
220 |
221 | // get the encryption
222 | $encryption = $user->encrypt();
223 |
224 | // handle the different
225 | // encryption types
226 | switch($encryption) {
227 | // sha1 encoded
228 | case 'sha1':
229 | return (sha1($password) == $user->password()) ? true : false;
230 | break;
231 | // md5 encoded
232 | case 'md5':
233 | return (md5($password) == $user->password()) ? true : false;
234 | break;
235 | // plain passwords
236 | default:
237 | return ($password == $user->password()) ? true : false;
238 | break;
239 | }
240 |
241 | // we should never get here
242 | // but let's make sure
243 | return false;
244 |
245 | }
246 |
247 | }
248 |
249 | class AuthUser extends obj {
250 |
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/plugins/instagram/instagram.php:
--------------------------------------------------------------------------------
1 | plugin for Kirby .
18 | * In order to use this plugin, you'll need to obtain an access token for API access. See the readme for more information.
19 | * @author Simon Albrecht
20 | * @version 1.0
21 | * @copyright (c) 2012 Simon Albrecht
22 | */
23 | class instagram {
24 | var $images;
25 | var $user;
26 |
27 | /**
28 | * Constructor. Loads the data from the Instagram API.
29 | * @param string The access token.
30 | * @param integer The number of shots that will be loaded.
31 | * @param boolean Chache enabled.
32 | * @param integer How many seconds until the cache expires.
33 | * @param string The user-id of the user or 'self' for your own account.
34 | */
35 | function __construct($_token = '', $_count = 10, $_cache = true, $_cache_expire = 3600, $_user = 'self') {
36 | // Init
37 | $this->images = array();
38 | $this->user = new stdClass;
39 |
40 | // Check if a token is provided
41 | if (trim($_token) != '') {
42 |
43 | // Construct the API url…
44 | // http://instagr.am/developer/endpoints/users/
45 | $url = "https://api.instagram.com/v1/users/{$_user}/media/recent/?access_token={$_token}&count={$_count}";
46 |
47 | // Create cache directory if it doesn't exist yet
48 | if ($_cache) {
49 | dir::make(c::get('root.cache') . '/instagram');
50 | }
51 |
52 | $images_cache_id = 'instagram/images.' . md5($_token) . '.' . $_count . '.php';
53 | $images_cache_data = false;
54 |
55 | // Try to fetch data from cache
56 | if ($_cache) {
57 | $images_cache_data = (cache::modified($images_cache_id) < time() - $_cache_expire) ? false : cache::get($images_cache_id);
58 | }
59 |
60 | // Load data from the API if the cache expired or the cache is empty
61 | if (empty($images_cache_data)) {
62 | $data = $this->fetch_data($url);
63 | $photos = json_decode($data);
64 |
65 | // Set new data for the cache
66 | if ($_cache) {
67 | cache::set($images_cache_id, $photos);
68 | }
69 | } else {
70 | $photos = $images_cache_data;
71 | }
72 |
73 | // Process the images
74 | for ($i = 0; $i < $_count; $i++) {
75 | if (isset($photos->data[$i]) && count($photos->data) > 0) {
76 |
77 | // Get the user's data from the first image
78 | if ($i == 0) {
79 | $this->user->username = $photos->data[$i]->user->username;
80 | $this->user->full_name = $photos->data[$i]->user->full_name;
81 | $this->user->picture = $photos->data[$i]->user->profile_picture;
82 | }
83 |
84 | // create a new object for each image
85 | $obj = new stdClass;
86 |
87 | $obj->link = $photos->data[$i]->link;
88 | $obj->comments = @$photos->data[$i]->comments->count;
89 | $obj->likes = @$photos->data[$i]->likes->count;
90 | $obj->created = $photos->data[$i]->created_time;
91 | $obj->thumb = @$photos->data[$i]->images->thumbnail->url;
92 | $obj->url = @$photos->data[$i]->images->standard_resolution->url;
93 | $obj->image_lowres = @$photos->data[$i]->images->low_resolution->url;
94 | $obj->filter = $photos->data[$i]->filter;
95 | $obj->location = @$photos->data[$i]->location->name;
96 | $obj->latitude = @$photos->data[$i]->location->latitude;
97 | $obj->longitude = @$photos->data[$i]->location->longitude;
98 | $obj->tags = array();
99 |
100 | // attach the new object to the array
101 | $this->images[$i] = $obj;
102 |
103 | // Process tags
104 | for ($j = 0; $j < count($photos->data[$i]->tags); $j++) {
105 | $this->images[$i]->tags[$j] = $photos->data[$i]->tags[$j];
106 | }
107 | }
108 | }
109 | } else {
110 | throw new Exception('$_token MUST be set!');
111 | }
112 | }
113 |
114 | /**
115 | * Returns the images that were loaded from the API.
116 | * @return array Array of objects containing all the photo's data.
117 | */
118 | function images() {
119 | return $this->images;
120 | }
121 |
122 | /**
123 | * Returns information about the user.
124 | * @return object Object with information about the user.
125 | */
126 | function user() {
127 | return $this->user;
128 | }
129 |
130 | /**
131 | * Fetches data from an url.
132 | * @param string The url from where data should be fetched.
133 | * @return object The data loaded from the url
134 | */
135 | protected function fetch_data($url = null) {
136 | if (!is_null($url)) {
137 |
138 | // Init CURL
139 | $handler = curl_init();
140 |
141 | // CURL options
142 | curl_setopt($handler, CURLOPT_URL, $url);
143 | curl_setopt($handler, CURLOPT_RETURNTRANSFER, 1);
144 |
145 | // Load data & close connection
146 | $data = curl_exec($handler);
147 | curl_close($handler);
148 |
149 | return $data;
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/plugins/thumb/thumb.php:
--------------------------------------------------------------------------------
1 | tag() : $thumb->url();
6 | }
7 |
8 | class thumb {
9 |
10 | var $obj = null;
11 | var $root = false;
12 | var $url = false;
13 | var $sourceWidth = 0;
14 | var $sourceHeight = 0;
15 | var $width = 0;
16 | var $height = 0;
17 | var $tmpWidth = 0;
18 | var $tmpHeight = 0;
19 | var $maxWidth = 0;
20 | var $maxHeight = 0;
21 | var $mime = false;
22 | var $status = array();
23 | var $upscale = false;
24 | var $quality = 100;
25 | var $alt = false;
26 | var $crop = false;
27 | var $grayscale = false;
28 | var $datauri = false;
29 |
30 | function __construct($image, $options=array()) {
31 |
32 | $this->root = c::get('thumb.cache.root', c::get('root') . '/thumbs');
33 | $this->url = c::get('thumb.cache.url', c::get('url') . '/thumbs');
34 |
35 | if(!$image) return false;
36 |
37 | $this->obj = $image;
38 |
39 | // set some values from the image
40 | $this->sourceWidth = $this->obj->width();
41 | $this->sourceHeight = $this->obj->height();
42 | $this->width = $this->sourceWidth;
43 | $this->height = $this->sourceHeight;
44 | $this->source = $this->obj->root();
45 | $this->mime = $this->obj->mime();
46 |
47 | // set the max width and height
48 | $this->maxWidth = @$options['width'];
49 | $this->maxHeight = @$options['height'];
50 |
51 | // set crop on/off
52 | $this->crop = @$options['crop'];
53 |
54 | // set greyscale on/off
55 | $this->grayscale = @$options['grayscale'];
56 |
57 | // set the quality
58 | $this->quality = a::get($options, 'quality', c::get('thumb.quality', 100));
59 |
60 | // set the default upscale behavior
61 | $this->upscale = a::get($options, 'upscale', c::get('thumb.upscale', false));
62 |
63 | // set datauri on/off
64 | $this->datauri = a::get($options, 'datauri', c::get('thumb.datauri', false));
65 |
66 | // set the alt text
67 | $this->alt = a::get($options, 'alt', $this->obj->name());
68 |
69 | // set the className text
70 | $this->className = @$options['class'];
71 |
72 | // set the new size
73 | $this->size();
74 |
75 | // create the thumbnail
76 | $this->create();
77 |
78 | }
79 |
80 | function tag() {
81 |
82 | if(!$this->obj) return false;
83 |
84 | $class = (!empty($this->className)) ? ' class="' . $this->className . '"' : '';
85 |
86 | return '';
87 |
88 | }
89 |
90 | function filename() {
91 |
92 | $options = false;
93 |
94 | $options .= ($this->maxWidth) ? '.' . $this->maxWidth : '.' . 0;
95 | $options .= ($this->maxHeight) ? '.' . $this->maxHeight : '.' . 0;
96 | $options .= ($this->upscale) ? '.' . $this->upscale : '.' . 0;
97 | $options .= ($this->crop) ? '.' . $this->crop : '.' . 0;
98 | $options .= ($this->grayscale) ? '.' . $this->grayscale : '.' . 0;
99 | $options .= '.' . $this->quality;
100 |
101 | return md5($this->source) . $options . '.' . $this->obj->extension;
102 |
103 | }
104 |
105 | function file() {
106 | return $this->root . '/' . $this->filename();
107 | }
108 |
109 | function url() {
110 | if($this->datauri == true) {
111 | return (error($this->status)) ? $this->obj->url() : 'data:' . $this->mime . ';base64,' . base64_encode(file_get_contents($this->file()));
112 | } else {
113 | return (error($this->status)) ? $this->obj->url() : $this->url . '/' . $this->filename() . '?' . filemtime($this->file());
114 | }
115 | }
116 |
117 | function size() {
118 |
119 | $maxWidth = $this->maxWidth;
120 | $maxHeight = $this->maxHeight;
121 | $upscale = $this->upscale;
122 |
123 | if($this->crop == true) {
124 |
125 | if(!$maxWidth) $maxWidth = $maxHeight;
126 | if(!$maxHeight) $maxHeight = $maxWidth;
127 |
128 | $sourceRatio = size::ratio($this->sourceWidth, $this->sourceHeight);
129 | $thumbRatio = size::ratio($maxWidth, $maxHeight);
130 |
131 | if($sourceRatio > $thumbRatio) {
132 | // fit the height of the source
133 | $size = size::fit_height($this->sourceWidth, $this->sourceHeight, $maxHeight, true);
134 | } else {
135 | // fit the height of the source
136 | $size = size::fit_width($this->sourceWidth, $this->sourceHeight, $maxWidth, true);
137 | }
138 |
139 | $this->tmpWidth = $size['width'];
140 | $this->tmpHeight = $size['height'];
141 | $this->width = $maxWidth;
142 | $this->height = $maxHeight;
143 |
144 | return $size;
145 |
146 | }
147 |
148 | // if there's a maxWidth and a maxHeight
149 | if($maxWidth && $maxHeight) {
150 |
151 | // if the source width is bigger then the source height
152 | // we need to fit the width
153 | if($this->sourceWidth > $this->sourceHeight) {
154 | $size = size::fit_width($this->sourceWidth, $this->sourceHeight, $maxWidth, $upscale);
155 |
156 | // do another check for the maxHeight
157 | if($size['height'] > $maxHeight) $size = size::fit_height($size['width'], $size['height'], $maxHeight);
158 |
159 | } else {
160 | $size = size::fit_height($this->sourceWidth, $this->sourceHeight, $maxHeight, $upscale);
161 |
162 | // do another check for the maxWidth
163 | if($size['width'] > $maxWidth) $size = size::fit_width($size['width'], $size['height'], $maxWidth);
164 |
165 | }
166 |
167 | } elseif($maxWidth) {
168 | $size = size::fit_width($this->sourceWidth, $this->sourceHeight, $maxWidth, $upscale);
169 | } elseif($maxHeight) {
170 | $size = size::fit_height($this->sourceWidth, $this->sourceHeight, $maxHeight, $upscale);
171 | } else {
172 | $size = array('width' => $this->sourceWidth, 'height' => $this->sourceHeight);
173 | }
174 |
175 | $this->width = $size['width'];
176 | $this->height = $size['height'];
177 |
178 | return $size;
179 |
180 | }
181 |
182 | function create() {
183 |
184 | $file = $this->file();
185 |
186 | if(!function_exists('gd_info')) return $this->status = array(
187 | 'status' => 'error',
188 | 'msg' => 'GD Lib is not installed'
189 | );
190 |
191 | if(file_exists($file) && (filectime($this->source) < filectime($file) || filemtime($this->source) < filemtime($file))) return $this->status = array(
192 | 'status' => 'success',
193 | 'msg' => 'The file exists'
194 | );
195 |
196 | if(!is_writable(dirname($file))) return $this->status = array(
197 | 'status' => 'error',
198 | 'msg' => 'The image file is not writable'
199 | );
200 |
201 | switch($this->mime) {
202 | case 'image/jpeg':
203 | $image = @imagecreatefromjpeg($this->source);
204 | break;
205 | case 'image/png':
206 | $image = @imagecreatefrompng($this->source);
207 | break;
208 | case 'image/gif':
209 | $image = @imagecreatefromgif($this->source);
210 | break;
211 | default:
212 | return $this->status = array(
213 | 'status' => 'error',
214 | 'msg' => 'The image mime type is invalid'
215 | );
216 | break;
217 | }
218 |
219 | if(!$image) return array(
220 | 'status' => 'error',
221 | 'msg' => 'The image could not be created'
222 | );
223 |
224 | // make enough memory available to scale bigger images (option should be something like 36M)
225 | if(c::get('thumb.memory')) ini_set('memory_limit', c::get('thumb.memory'));
226 |
227 | if($this->crop == true) {
228 |
229 | // Starting point of crop
230 | $startX = floor($this->tmpWidth / 2) - floor($this->width / 2);
231 | $startY = floor($this->tmpHeight / 2) - floor($this->height / 2);
232 |
233 | // Adjust crop size if the image is too small
234 | if($startX < 0) $startX = 0;
235 | if($startY < 0) $startY = 0;
236 |
237 | // create a temporary resized version of the image first
238 | $thumb = imagecreatetruecolor($this->tmpWidth, $this->tmpHeight);
239 | imagesavealpha($thumb, true);
240 | $color = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
241 | imagefill($thumb, 0, 0, $color);
242 | imagecopyresampled($thumb, $image, 0, 0, 0, 0, $this->tmpWidth, $this->tmpHeight, $this->sourceWidth, $this->sourceHeight);
243 |
244 | // crop that image afterwards
245 | $cropped = imagecreatetruecolor($this->width, $this->height);
246 | imagesavealpha($cropped, true);
247 | $color = imagecolorallocatealpha($cropped, 0, 0, 0, 127);
248 | imagefill($cropped, 0, 0, $color);
249 | imagecopyresampled($cropped, $thumb, 0, 0, $startX, $startY, $this->tmpWidth, $this->tmpHeight, $this->tmpWidth, $this->tmpHeight);
250 | imagedestroy($thumb);
251 |
252 | // reasign the variable
253 | $thumb = $cropped;
254 |
255 | } else {
256 | $thumb = imagecreatetruecolor($this->width, $this->height);
257 | imagesavealpha($thumb, true);
258 | $color = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
259 | imagefill($thumb, 0, 0, $color);
260 | imagecopyresampled($thumb, $image, 0, 0, 0, 0, $this->width, $this->height, $this->sourceWidth, $this->sourceHeight);
261 | }
262 |
263 | if($this->grayscale == true) {
264 | imagefilter($thumb, IMG_FILTER_GRAYSCALE);
265 | }
266 |
267 | switch($this->mime) {
268 | case 'image/jpeg': imagejpeg($thumb, $file, $this->quality); break;
269 | case 'image/png' : imagepng($thumb, $file, 0); break;
270 | case 'image/gif' : imagegif($thumb, $file); break;
271 | }
272 |
273 | imagedestroy($thumb);
274 |
275 | return $this->status = array(
276 | 'status' => 'success',
277 | 'msg' => 'The image has been created',
278 | );
279 |
280 | }
281 |
282 | }
--------------------------------------------------------------------------------
/plugins/contactform/lib/email.php:
--------------------------------------------------------------------------------
1 | 'John Doe ',
28 | * 'from' => 'Your Name ',
29 | * 'subject' => 'My first Email',
30 | * 'body' => 'Hello my friend! How are things going?'
31 | * ));
32 | *
33 | * Author: Bastian Allgeier
34 | * License: MIT License
35 | *
36 | */
37 |
38 |
39 | function email($params=array()) {
40 | $email = new email($params);
41 | return $email->send();
42 | }
43 |
44 |
45 | class email {
46 |
47 | var $options = array();
48 |
49 | function __construct($params=array()) {
50 |
51 | $defaults = array(
52 | // global defaults
53 | 'use' => c::get('email.use', 'mail'),
54 | 'from' => c::get('email.from'),
55 | 'replyto' => c::get('email.replyto'),
56 | 'subject' => c::get('email.subject'),
57 | 'to' => c::get('email.to'),
58 |
59 | // postmark api defaults
60 | 'postmark.key' => c::get('email.postmark.key'),
61 | 'postmark.test' => c::get('email.postmark.test'),
62 |
63 | // amazon api defaults
64 | 'amazon.key' => c::get('email.amazon.key'),
65 | 'amazon.secret' => c::get('email.amazon.secret'),
66 | );
67 |
68 | $this->options = array_merge($defaults, $params);
69 |
70 | // set the from address as the replyto address if no replyto address is specified
71 | if(empty($this->options['replyto'])) $this->options['replyto'] = $this->options['from'];
72 |
73 | }
74 |
75 | public function send() {
76 |
77 | if(c::get('email.disabled')) return array(
78 | 'status' => 'error',
79 | 'msg' => l::get('email.disabled', 'Email has been disabled')
80 | );
81 |
82 | if(!v::email($this->extractAddress($this->options['from']))) return array(
83 | 'status' => 'error',
84 | 'msg' => l::get('email.error.invalid.sender', 'Invalid sender'),
85 | );
86 |
87 | if(!v::email($this->extractAddress($this->options['to']))) return array(
88 | 'status' => 'error',
89 | 'msg' => l::get('email.error.invalid.recipient', 'Invalid recipient'),
90 | );
91 |
92 | if(!v::email($this->extractAddress($this->options['replyto']))) return array(
93 | 'status' => 'error',
94 | 'msg' => l::get('email.error.invalid.replyto', 'Invalid Reply-To Address'),
95 | );
96 |
97 | if(str::length($this->options['subject']) == 0) return array(
98 | 'status' => 'error',
99 | 'msg' => l::get('email.error.invalid.subject', 'The subject is missing'),
100 | );
101 |
102 | $method = 'sendWith' . str::ucfirst($this->options['use']);
103 |
104 | if(!method_exists(__CLASS__, $method)) return array(
105 | 'status' => 'error',
106 | 'msg' => l::get('email.error.invalid.mailer', 'This email service is not supported'),
107 | );
108 |
109 | return $this->$method();
110 |
111 | }
112 |
113 | private function sendWithPostmark() {
114 |
115 | if(!$this->options['postmark.key']) return array(
116 | 'status' => 'error',
117 | 'msg' => l::get('email.error.invalid.key', 'Invalid API key'),
118 | );
119 |
120 | // reset the api key if we are in test mode
121 | if($this->options['postmark.test']) $this->options['postmark.key'] = 'POSTMARK_API_TEST';
122 |
123 | $url = 'http://api.postmarkapp.com/email';
124 |
125 | $headers = array(
126 | 'Accept: application/json',
127 | 'Content-Type: application/json',
128 | 'X-Postmark-Server-Token: ' . $this->options['postmark.key']
129 | );
130 |
131 | $data = array(
132 | 'From' => $this->options['from'],
133 | 'To' => $this->options['to'],
134 | 'ReplyTo' => $this->options['replyto'],
135 | 'Subject' => $this->options['subject'],
136 | 'TextBody' => $this->options['body']
137 | );
138 |
139 | $response = $this->post($url, a::json($data), array('headers' => $headers));
140 | $code = @$response['http_code'];
141 |
142 | if($code != 200) return array(
143 | 'status' => 'error',
144 | 'msg' => l::get('email.error', 'The mail could not be sent!'),
145 | 'response' => $response
146 | );
147 |
148 | return array(
149 | 'status' => 'success',
150 | 'msg' => l::get('email.success', 'The mail has been sent'),
151 | 'response' => $response
152 | );
153 |
154 | }
155 |
156 | private function sendWithAmazon() {
157 |
158 | if(!$this->options['amazon.key']) return array(
159 | 'status' => 'error',
160 | 'msg' => l::get('email.error.invalid.key', 'Invalid API key'),
161 | );
162 |
163 | if(!$this->options['amazon.secret']) return array(
164 | 'status' => 'error',
165 | 'msg' => l::get('email.error.invalid.secret', 'Invalid API secret'),
166 | );
167 |
168 | $setup = array(
169 | 'Action' => 'SendEmail',
170 | 'Destination.ToAddresses.member.1' => $this->options['to'],
171 | 'ReplyToAddresses.member.1' => $this->options['replyto'],
172 | 'ReturnPath' => $this->options['replyto'],
173 | 'Source' => $this->options['from'],
174 | 'Message.Subject.Data' => $this->options['subject'],
175 | 'Message.Body.Text.Data' => $this->options['body']
176 | );
177 |
178 | $params = array();
179 |
180 | foreach($setup as $key => $value) {
181 | $params[] = $key . '=' . str_replace('%7E', '~', rawurlencode($value));
182 | }
183 |
184 | sort($params, SORT_STRING);
185 |
186 | $host = 'email.us-east-1.amazonaws.com';
187 | $url = 'https://' . $host . '/';
188 | $date = gmdate('D, d M Y H:i:s e');
189 | $signature = base64_encode(hash_hmac('sha256', $date, $this->options['amazon.secret'], true));
190 | $query = implode('&', $params);
191 | $headers = array();
192 |
193 | $auth = 'AWS3-HTTPS AWSAccessKeyId=' . $this->options['amazon.key'];
194 | $auth .= ',Algorithm=HmacSHA256,Signature=' . $signature;
195 |
196 | $headers[] = 'Date: ' . $date;
197 | $headers[] = 'Host: ' . $host;
198 | $headers[] = 'X-Amzn-Authorization: '. $auth;
199 | $headers[] = 'Content-Type: application/x-www-form-urlencoded';
200 |
201 | $response = $this->post($url, $query, array('headers' => $headers));
202 | $code = @$response['http_code'];
203 |
204 | if(!in_array($code, array(200, 201, 202, 204))) return array(
205 | 'status' => 'error',
206 | 'msg' => l::get('email.error', 'The mail could not be sent!'),
207 | 'response' => $response
208 | );
209 |
210 | return array(
211 | 'status' => 'success',
212 | 'msg' => l::get('email.success', 'The mail has been sent'),
213 | 'response' => $response
214 | );
215 |
216 | }
217 |
218 | private function sendWithMail() {
219 |
220 | $headers = array();
221 |
222 | $headers[] = 'From: ' . $this->options['from'];
223 | $headers[] = 'Reply-To: ' . $this->options['replyto'];
224 | $headers[] = 'Return-Path: ' . $this->options['replyto'];
225 | $headers[] = 'Message-ID: <' . time() . '-' . $this->options['from'] . '>';
226 | $headers[] = 'X-Mailer: PHP v' . phpversion();
227 | $headers[] = 'Content-Type: text/plain; charset=utf-8';
228 | $headers[] = 'Content-Transfer-Encoding: 8bit';
229 |
230 | ini_set('sendmail_from', $this->options['from']);
231 | $send = mail($this->options['to'], str::utf8($this->options['subject']), str::utf8($this->options['body']), implode("\r\n", $headers));
232 | ini_restore('sendmail_from');
233 |
234 | if(!$send) return array(
235 | 'status' => 'error',
236 | 'msg' => l::get('email.error', 'The mail could not be sent!')
237 | );
238 |
239 | return array(
240 | 'status' => 'success',
241 | 'msg' => l::get('email.success', 'The mail has been sent')
242 | );
243 |
244 | }
245 |
246 | private function extractAddress($string) {
247 | if(v::email($string)) return $string;
248 | preg_match('/<(.*?)>/i', $string, $array);
249 | $address = @$array[1];
250 | return (v::email($address)) ? $address : false;
251 | }
252 |
253 | private function post($url, $data=false, $options=array()) {
254 |
255 | $data = (is_array($data)) ? http_build_query($data) : $data;
256 |
257 | $defaults = array(
258 | 'timeout' => 10,
259 | 'headers' => array(),
260 | 'agent' => 'email/php',
261 | 'encoding' => 'utf-8'
262 | );
263 |
264 | $options = array_merge($defaults, $options);
265 | $ch = curl_init();
266 |
267 | $params = array(
268 | CURLOPT_URL => $url,
269 | CURLOPT_RETURNTRANSFER => true,
270 | CURLOPT_FOLLOWLOCATION => true,
271 | CURLOPT_ENCODING => $options['encoding'],
272 | CURLOPT_AUTOREFERER => true,
273 | CURLOPT_USERAGENT => $options['agent'],
274 | CURLOPT_CONNECTTIMEOUT => $options['timeout'],
275 | CURLOPT_TIMEOUT => $options['timeout'],
276 | CURLOPT_MAXREDIRS => 10,
277 | CURLOPT_SSL_VERIFYPEER => false,
278 | CURLOPT_SSL_VERIFYHOST => false,
279 | CURLOPT_POST => true,
280 | CURLOPT_POSTFIELDS => $data
281 | );
282 |
283 | if(!empty($options['headers'])) $params[CURLOPT_HTTPHEADER] = $options['headers'];
284 |
285 | curl_setopt_array($ch, $params);
286 |
287 | $content = curl_exec($ch);
288 | $error = curl_errno($ch);
289 | $message = curl_error($ch);
290 | $response = curl_getinfo($ch);
291 |
292 | curl_close($ch);
293 |
294 | $response['error'] = $error;
295 | $response['message'] = $message;
296 | $response['content'] = $content;
297 |
298 | if(a::get($response, 'error')) return array(
299 | 'status' => 'error',
300 | 'msg' => 'The remote request failed: ' . $response['message'],
301 | 'response' => $response
302 | );
303 |
304 | if(a::get($response, 'http_code') >= 400) return array(
305 | 'status' => 'error',
306 | 'msg' => 'The remote request failed - code: ' . $response['http_code'],
307 | 'code' => $response['http_code'],
308 | 'response' => $response
309 | );
310 |
311 | return $response;
312 |
313 | }
314 |
315 | }
--------------------------------------------------------------------------------
/plugins/dribbble/dribbble.php:
--------------------------------------------------------------------------------
1 | plugin for Kirby , that loads the shots of a player.
19 | * @author Simon Albrecht , caching added by Bastian Allgeier
20 | * @version 1.2
21 | * @copyright (c) 2012 Simon Albrecht
22 | */
23 | class dribbble {
24 |
25 | // Variables
26 | var $username;
27 | var $shots;
28 | var $likes;
29 | var $player;
30 |
31 | /**
32 | * Constructor. Loads the data from Dribbble when the object is constructed.
33 | * @param string $_username The username of the player.
34 | * @param integer $_number_of_shots The number of shots that will be loaded.
35 | * @param boolean $_fetch_likes If the likes of the user should be fetched in a second call.
36 | * @param integer If $_fetch_likes is true, then how many likes should be fetched.
37 | * @param boolean $cache Enable/disable caching. Cache is enabled by default
38 | * @param integer $refresh Seconds before the cache will be refreshed. Default is in hour (3600 seconds)
39 | */
40 | function __construct($_username = "s_albrecht", $_number_of_shots = 3, $_fetch_likes = false, $_number_of_likes = 3, $cache = true, $refresh = 3600) {
41 | // Init
42 | $this->username = $_username;
43 | $this->shots = array();
44 | $this->likes = array();
45 | $this->player = null;
46 |
47 | // Build URLs
48 | $base_url = "http://api.dribbble.com/players/" . $this->username;
49 | $shots_url = $base_url . "/shots";
50 | $likes_url = $base_url . "/likes";
51 |
52 | // create the cache directory if not there yet
53 | if($cache) dir::make(c::get('root.cache') . '/dribbble');
54 |
55 | // Process the data
56 | if ($_number_of_shots > 0) {
57 |
58 | // define a cache id
59 | $shots_cache_id = 'dribbble/shots.' . md5($this->username) . '.' . $_number_of_shots . '.php';
60 | $shots_cache_data = false;
61 |
62 | // try to fetch the data from cache
63 | if($cache) {
64 | $shots_cache_data = (cache::modified($shots_cache_id) < time()-$refresh) ? false : cache::get($shots_cache_id);
65 | }
66 |
67 | // if there's no data in the cache, load shots from the Dribbble API
68 | if(empty($shots_cache_data)) {
69 | $all_shots = $this->fetch_data($shots_url);
70 | $all_shots = json_decode($all_shots);
71 | $all_shots = $all_shots->shots;
72 |
73 | if($cache) cache::set($shots_cache_id, $all_shots);
74 |
75 | } else {
76 | $all_shots = $shots_cache_data;
77 | }
78 |
79 | // Only proceed if there is at least one shot.
80 | // If there's no shot, then player data can't be extracted from this API call
81 | // and must be extracted via /players/:id/ (maybe I'll implement that later)
82 | if (count($all_shots) > 0) {
83 |
84 | // Load shots data
85 | for ($i = 0; $i < $_number_of_shots; $i++) {
86 | if (!is_null($all_shots[$i])) {
87 | $this->shots[$i]->id = $all_shots[$i]->id;
88 | $this->shots[$i]->title = $all_shots[$i]->title;
89 | $this->shots[$i]->url = $all_shots[$i]->url;
90 | $this->shots[$i]->short_url = $all_shots[$i]->short_url;
91 | $this->shots[$i]->image = $all_shots[$i]->image_url;
92 | $this->shots[$i]->likes = $all_shots[$i]->likes_count;
93 | $this->shots[$i]->views = $all_shots[$i]->views_count;
94 | $this->shots[$i]->rebounds = $all_shots[$i]->rebounds_count;
95 | $this->shots[$i]->comments = $all_shots[$i]->comments_count;
96 | $this->shots[$i]->created = $all_shots[$i]->created_at;
97 | }
98 | }
99 |
100 | // Process player data
101 | $this->player->id = $all_shots[0]->player->id;
102 | $this->player->name = $all_shots[0]->player->name;
103 | $this->player->username = $all_shots[0]->player->username;
104 | $this->player->url = $all_shots[0]->player->url;
105 | $this->player->avatar_url = $all_shots[0]->player->avatar_url;
106 | $this->player->twitter = $all_shots[0]->player->twitter_screen_name;
107 | $this->player->location = $all_shots[0]->player->location;
108 | $this->player->followers = $all_shots[0]->player->followers_count;
109 | $this->player->following = $all_shots[0]->player->following_count;
110 | $this->player->likes = $all_shots[0]->player->likes_count;
111 | }
112 | }
113 |
114 | // Fetch all likes of the user (needs another API call).
115 | // If you only want to fetch the likes, not the shots, then set $_number_of_shots to 0.
116 | if ($_fetch_likes && $_number_of_likes > 0) {
117 |
118 | // define a cache id
119 | $likes_cache_id = 'dribbble/likes.' . md5($this->username) . '.' . $_number_of_likes . '.php';
120 | $likes_cache_data = false;
121 |
122 | // try to fetch the data from cache
123 | if($cache) {
124 | $likes_cache_data = (cache::modified($likes_cache_id) < time()-$refresh) ? false : cache::get($likes_cache_id);
125 | }
126 |
127 | // if there's no data in the cache, load likes from the Dribbble API
128 | if(empty($likes_cache_data)) {
129 | $all_likes = $this->fetch_data($likes_url);
130 | $all_likes = json_decode($all_likes);
131 | $all_likes = $all_likes->shots;
132 |
133 | if($cache) cache::set($likes_cache_id, $all_likes);
134 |
135 | } else {
136 | $all_likes = $likes_cache_data;
137 | }
138 |
139 |
140 | // Process likes
141 | for ($i = 0; $i < $_number_of_likes; $i++) {
142 | if (!is_null($all_likes[$i])) {
143 | $this->likes[$i]->id = $all_likes[$i]->id;
144 | $this->likes[$i]->title = $all_likes[$i]->title;
145 | $this->likes[$i]->url = $all_likes[$i]->url;
146 | $this->likes[$i]->short_url = $all_likes[$i]->short_url;
147 | $this->likes[$i]->image = $all_likes[$i]->image_url;
148 | $this->likes[$i]->likes = $all_likes[$i]->likes_count;
149 | $this->likes[$i]->views = $all_likes[$i]->views_count;
150 | $this->likes[$i]->rebounds = $all_likes[$i]->rebounds_count;
151 | $this->likes[$i]->comments = $all_likes[$i]->comments_count;
152 | $this->likes[$i]->created = $all_likes[$i]->created_at;
153 |
154 | // Process the user the like belongs to
155 | $this->likes[$i]->player->id = $all_likes[$i]->player->id;
156 | $this->likes[$i]->player->name = $all_likes[$i]->player->name;
157 | $this->likes[$i]->player->username = $all_likes[$i]->player->username;
158 | $this->likes[$i]->player->url = $all_likes[$i]->player->url;
159 | $this->likes[$i]->player->avatar_url = $all_likes[$i]->player->avatar_url;
160 | $this->likes[$i]->player->twitter = $all_likes[$i]->player->twitter_screen_name;
161 | $this->likes[$i]->player->location = $all_likes[$i]->player->location;
162 | $this->likes[$i]->player->followers = $all_likes[$i]->player->followers_count;
163 | $this->likes[$i]->player->following = $all_likes[$i]->player->following_count;
164 | $this->likes[$i]->player->likes = $all_likes[$i]->player->likes_count;
165 | }
166 | }
167 | }
168 | }
169 |
170 | /**
171 | * Returns the shots of the player.
172 | * @return array Array of objects that contain the shots data.
173 | */
174 | function shots() {
175 | return $this->shots;
176 | }
177 |
178 | /**
179 | * Returns the likes of the player.
180 | * @return array Array of objects that contain the likes data.
181 | */
182 | function likes() {
183 | return $this->likes;
184 | }
185 |
186 | /**
187 | * Returns an object containing all the data of the player.
188 | * @return object Object containing all the player's data.
189 | */
190 | function player() {
191 | return $this->player;
192 | }
193 |
194 | /**
195 | * Returns the username.
196 | * @return string The username used to construct this class.
197 | */
198 | function username() {
199 | return $this->username;
200 | }
201 |
202 | /**
203 | * Fetches data from an url.
204 | * @param string The url from where data should be fetched.
205 | * @return object The data loaded from the url
206 | */
207 | protected function fetch_data($url = null) {
208 | if (!is_null($url)) {
209 |
210 | // Init CURL
211 | $handler = curl_init();
212 |
213 | // CURL options
214 | curl_setopt($handler, CURLOPT_URL, $url);
215 | curl_setopt($handler, CURLOPT_RETURNTRANSFER, 1);
216 |
217 | // Load data & close connection
218 | $data = curl_exec($handler);
219 | curl_close($handler);
220 |
221 | return $data;
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------