diff --git a/.gitignore b/.gitignore
index 01cdfee..8e11af4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,23 +10,27 @@ desktop.ini
.DS_Store
._*
+# Travis
+/build/phpdoc-*/
+/build/phpdoc-*.git/
+
# Composer
-composer.lock
-composer.phar
-vendor/*
+/composer.lock
+/composer.phar
+/vendor
# User config
-config/config.php
+/config/config.php
# User themes
-themes/*
-!themes/default/*
+/themes/*
+!/themes/default
# User plugins
-plugins/*
-!plugins/0?-*
-!plugins/1?-*
-!plugins/DummyPlugin.php
+/plugins/*
+!/plugins/0?-*
+!/plugins/1?-*
+!/plugins/DummyPlugin.php
# User content
-content/*
+/content
diff --git a/.travis.yml b/.travis.yml
index 01b5c42..aa0af02 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,12 +8,29 @@ php:
- hhvm
- nightly
+matrix:
+ allow_failures:
+ - php: nightly
+ fast-finish: true
+
+install:
+ - composer install
+
+before_script:
+ - export PATH="$TRAVIS_BUILD_DIR/build:$TRAVIS_BUILD_DIR/vendor/bin:$PATH"
+
script:
- - find . -type f -name '*.php' -print0 | xargs -0 -I file php -l file > /dev/null
+ - phpcs --standard=phpcs.xml "$TRAVIS_BUILD_DIR"
+
+after_success:
+ - deploy-phpdoc-branch.sh
before_deploy:
- - composer install
- - tar -czf "pico-release-$TRAVIS_TAG.tar.gz" .htaccess README.md CHANGELOG.md CONTRIBUTING.md composer.json composer.lock LICENSE config content-sample lib plugins themes vendor index.php
+ - deploy-phpdoc-release.sh
+ - composer install --no-dev --optimize-autoloader
+ - find vendor/ -type d -path 'vendor/*/*/.git' -print0 | xargs -0 rm -rf
+ - mv index.php.dist index.php
+ - tar -czf "pico-release-$TRAVIS_TAG.tar.gz" README.md LICENSE CONTRIBUTING.md CHANGELOG.md composer.json composer.lock config content-sample lib plugins themes vendor .htaccess index.php
deploy:
provider: releases
@@ -21,9 +38,7 @@ deploy:
file: pico-release-$TRAVIS_TAG.tar.gz
skip_cleanup: true
on:
- repo: picocms/Pico
tags: true
php: 5.3
sudo: false
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b920e9..d02dafe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,10 +12,24 @@ Released: -
want to parse the contents of a page, use the `content` filter instead
* [New] New `sort_by` filter to sort a array by a specified key or key path
* [New] New `map` filter to get the values of the given key or key path
+* [New] New PHP version check in `index.php`
+* [Changed] Improve documentation
+* [Changed] Improve table styling in default theme
+* [Changed] Update composer version constraints; almost all dependencies will
+ have pending updates, run `composer update`
+* [Changed] Throw a RuntimeException when the `content` dir isn't accessible
* [Changed] Reuse `ParsedownExtra` object; new `onParsedownRegistration` event
+* [Changed] `$config['rewrite_url']` is now always available
+* [Changed] `DummyPlugin` class is now final
+* [Changed] Various small improvements and changes...
+* [Fixed] `PicoDeprecated`: Sanitize `content_dir` and `base_url` options when
+ reading `config.php` in Picos root dir
* [Fixed] Replace `urldecode()` (deprecated RFC 1738) with `rawurldecode()`
(RFC 3986) in `Page::evaluateRequestUrl()`
* [Fixed] #272: Encode URLs using `rawurlencode()` in `Pico::getPageUrl()`
+* [Fixed] #274: Prevent double slashes in `base_url`
+* [Fixed] #285: Make `index.php` work when installed as a composer dependency
+* [Fixed] #291: Force `Pico::$requestUrl` to have no leading/trailing slash
```
### Version 1.0.0-beta.1
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2aa4241..b849472 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,7 +12,9 @@ Issues
If you want to report an *issue* with Pico's core, please create a new [Issue](https://github.com/picocms/Pico/issues) on GitHub. Concerning problems with plugins or themes, please refer to the website of the developer of this plugin or theme.
-Before creating a [new Issue on GitHub](https://github.com/picocms/Pico/issues/new), please make sure the problem wasn't reported yet using [GitHubs search engine](https://github.com/picocms/Pico/search?type=Issues). Please describe your issue as clear as possible and always include steps to reproduce the problem.
+Before creating a [new Issue on GitHub](https://github.com/picocms/Pico/issues/new), please make sure the problem wasn't reported yet using [GitHubs search engine](https://github.com/picocms/Pico/search?type=Issues).
+
+Please describe your issue as clear as possible and always include the *Pico version* you're using. Provided that you're using *plugins*, include a list of them too. We need information about the *actual and expected behavior*, the *steps to reproduce* the problem, and what steps have you taken to resolve the problem by yourself (i.e. *your own troubleshooting*)?
Contributing code
-----------------
@@ -45,7 +47,7 @@ With this command you can specify a file or folder to limit which files it will
### Keep documentation in sync
-Pico accepts the problems of having redundant documentation on different places (concretely Pico's inline user docs, the `README.md` and the website) for the sake of a better user experience. When updating the docs, please make sure the keep them in sync.
+Pico accepts the problems of having redundant documentation on different places (concretely Pico's inline user docs, the `README.md` and the website) for the sake of a better user experience. When updating the docs, please make sure to keep them in sync.
If you update the [`README.md`](https://github.com/picocms/Pico/blob/master/README.md) or [`content-sample/index.md`](https://github.com/picocms/Pico/blob/master/content-sample/index.md), please make sure to update the corresponding files in the [`_docs`](https://github.com/picocms/Pico/tree/gh-pages/_docs/) folder of the `gh-pages` branch (i.e. [Pico's website](http://picocms.org/docs.html)) and vice versa. Unfortunately this involves three (!) different markdown parsers. If you're experiencing problems, use Pico's [`erusev/parsedown-extra`](https://github.com/erusev/parsedown-extra) as a reference. You can try to make the contents compatible to [Redcarpet](https://github.com/vmg/redcarpet) by yourself, otherwise please address the issues in your pull request message and we'll take care of it.
@@ -78,44 +80,18 @@ As soon as development reaches a point where feedback is appreciated, a pull req
Build & Release process
-----------------------
-This is work in progress. Please refer to [#268](https://github.com/picocms/Pico/issues/268) for details.
+We're using [Travis CI](https://travis-ci.com) to automate the build & release process of Pico. It generates and deploys [phpDoc](http://phpdoc.org) class docs for new releases and on every commit to the `master` branch. Travis also prepares new releases by generating Pico's pre-built packages and uploading them to GitHub. Please refer to [our `.travis.yml`](https://github.com/picocms/Pico/blob/master/.travis.yml) for details.
-
+Travis CI will draft the new [release on GitHub](https://github.com/picocms/Pico/releases) automatically, but will require you to manually amend the descriptions formatting. The latest Pico version is always available at https://github.com/picocms/Pico/releases/latest, so please make sure to publish this URL rather than version-specific URLs. [Packagist](http://packagist.org/packages/picocms/pico) will be updated automatically.
diff --git a/README.md b/README.md
index 520d937..1f32a64 100644
--- a/README.md
+++ b/README.md
@@ -46,11 +46,11 @@ Upgrading Pico is very easy: You just have to replace all of Pico's files - that
Pico follows [Semantic Versioning 2.0][SemVer] and uses version numbers like `MAJOR`.`MINOR`.`PATCH`. When we update...
-- the `PATCH` version (e.g. `1.0.0` to `1.0.1`), we made backwards-compatible bug fixes. It's then sufficient to extract [Pico's latest release][LatestRelease] to your existing installation directory and overwriting all files.
+- the `PATCH` version (e.g. `1.0.0` to `1.0.1`), we made backwards-compatible bug fixes. It's then sufficient to extract [Pico's latest release][LatestRelease] to your existing installation directory and overwriting all files. Alternatively you can either use the [*source code* of Pico's latest release][LatestRelease] or pull from Pico's Git repository, but are then required to update Pico's [composer][] dependencies manually by running `php composer.phar update`.
- the `MINOR` version (e.g. `1.0` to `1.1`), we added functionality in a backwards-compatible manner, but anyway recommend you to "install" Pico newly. Backup all of your files, empty your installation directory and install Pico as elucidated above. You can then copy your `config/config.php` and `content` directory without any change. If applicable, you can also copy the folder of your custom theme within the `themes` directory. Provided that you're using plugins, also copy all of your plugins from the `plugins` directory.
-- the `MAJOR` version (e.g. `1.0` to `2.0`), a appropriate upgrade tutorial will be provided.
+- the `MAJOR` version (e.g. `1.0` to `2.0`), we made incompatible API changes. We will then provide a appropriate upgrade tutorial.
Upgrading Pico 0.8 or 0.9 to Pico 1.0 is a special case. The new `PicoDeprecated` plugin ensures backwards compatibility, so you basically can follow the above upgrade instructions as if we updated the `MINOR` version. However, we recommend you to take some further steps to confine the necessity of `PicoDeprecated` as far as possible. For more information about what has changed with Pico 1.0 and a step-by-step upgrade tutorial, please refer to the [upgrade page of our website][HelpUpgrade].
@@ -115,6 +115,6 @@ You want to contribute to Pico? We really appreciate that! You can help make Pic
[IssuesSearch]: https://github.com/picocms/Pico/search?type=Issues
[PullRequests]: https://github.com/picocms/Pico/pulls
[ContributionGuidelines]: https://github.com/picocms/Pico/blob/master/CONTRIBUTING.md
-[EditInlineDocs]: https://github.com/picocms/Pico/blob/master/content-sample/index.md
+[EditInlineDocs]: https://github.com/picocms/Pico/edit/master/content-sample/index.md
[EditUserDocs]: https://github.com/picocms/Pico/tree/gh-pages/_docs
[EditDevDocs]: https://github.com/picocms/Pico/tree/gh-pages/_plugin-dev
diff --git a/build/deploy-phpdoc-branch.sh b/build/deploy-phpdoc-branch.sh
new file mode 100755
index 0000000..f08063d
--- /dev/null
+++ b/build/deploy-phpdoc-branch.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+if [ "$TRAVIS_PHP_VERSION" != "5.3" ]; then
+ echo "Skipping phpDoc deployment because this is not on the required runtime"
+ exit
+fi
+
+if [[ ",$DEPLOY_PHPDOC_BRANCHES," != *,"$TRAVIS_BRANCH",* ]]; then
+ echo "Skipping phpDoc deployment because this branch ($TRAVIS_BRANCH) is not permitted to deploy"
+ exit
+fi
+
+if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
+ echo "Skipping phpDoc deployment because this pull request (#$TRAVIS_PULL_REQUEST) is not permitted to deploy"
+ exit
+fi
+
+PHPDOC_ID="${TRAVIS_BRANCH//\//_}"
+
+generate-phpdoc.sh \
+ "$TRAVIS_BUILD_DIR" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
+ "Pico 1.0 API Documentation ($TRAVIS_BRANCH branch)"
+[ $? -eq 0 ] || exit 1
+
+deploy-phpdoc.sh \
+ "$TRAVIS_REPO_SLUG" "heads/$TRAVIS_BRANCH @ $TRAVIS_COMMIT" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
+ "$TRAVIS_REPO_SLUG" "gh-pages" "phpDoc/$PHPDOC_ID"
+[ $? -eq 0 ] || exit 1
diff --git a/build/deploy-phpdoc-release.sh b/build/deploy-phpdoc-release.sh
new file mode 100755
index 0000000..721f3a4
--- /dev/null
+++ b/build/deploy-phpdoc-release.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+[ "$DEPLOY_PHPDOC_RELEASES" == "true" ] || exit
+
+PHPDOC_ID="${TRAVIS_BRANCH//\//_}"
+
+generate-phpdoc.sh \
+ "$TRAVIS_BUILD_DIR" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
+ "Pico 1.0 API Documentation ($TRAVIS_TAG)"
+[ $? -eq 0 ] || exit 1
+
+deploy-phpdoc.sh \
+ "$TRAVIS_REPO_SLUG" "tags/$TRAVIS_TAG" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
+ "$TRAVIS_REPO_SLUG" "gh-pages" "phpDoc/$PHPDOC_ID"
+[ $? -eq 0 ] || exit 1
diff --git a/build/deploy-phpdoc.sh b/build/deploy-phpdoc.sh
new file mode 100755
index 0000000..26d06b8
--- /dev/null
+++ b/build/deploy-phpdoc.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+set -e
+
+# base variables
+APP_NAME="$(basename "$0")"
+BASE_PWD="$PWD"
+
+# environment variables
+# GITHUB_OAUTH_TOKEN GitHub authentication token, see https://github.com/settings/tokens
+
+# parameters
+SOURCE_REPO_SLUG="$1" # source GitHub repo (e.g. picocms/Pico)
+SOURCE_REF="$2" # source reference (either "[ref] @ [commit]" or "[ref]",
+ # [ref] can be e.g. heads/master or tags/v1.0.0)
+SOURCE_DIR="$3" # absolute source path
+TARGET_REPO_SLUG="$4" # target GitHub repo (e.g. picocms/Pico)
+TARGET_BRANCH="$5" # target branch (e.g. gh-pages)
+TARGET_DIR="$6" # relative target path
+
+# print parameters
+echo "Deploying phpDocs..."
+printf 'SOURCE_REPO_SLUG="%s"\n' "$SOURCE_REPO_SLUG"
+printf 'SOURCE_REF="%s"\n' "$SOURCE_REF"
+printf 'SOURCE_DIR="%s"\n' "$SOURCE_DIR"
+printf 'TARGET_REPO_SLUG="%s"\n' "$TARGET_REPO_SLUG"
+printf 'TARGET_BRANCH="%s"\n' "$TARGET_BRANCH"
+printf 'TARGET_DIR="%s"\n' "$TARGET_DIR"
+echo
+
+# evaluate source reference
+if [[ "$SOURCE_REF" == *" @ "* ]]; then
+ SOURCE_REF_TYPE="commit"
+ SOURCE_REF_HEAD="${SOURCE_REF% @ *}"
+ SOURCE_REF_COMMIT="${SOURCE_REF##* @ }"
+
+ if ! git check-ref-format "$SOURCE_REF_HEAD" || ! git rev-parse --verify "$SOURCE_REF_COMMIT" > /dev/null; then
+ echo "FATAL: $APP_NAME source reference '$SOURCE_REF' is invalid" >&2
+ exit 1
+ fi
+elif git check-ref-format "$SOURCE_REF"; then
+ SOURCE_REF_TYPE="ref"
+else
+ echo "FATAL: $APP_NAME source reference '$SOURCE_REF' is invalid" >&2
+ exit 1
+fi
+
+# clone repo
+printf 'Cloning repo...\n'
+GIT_DIR="$SOURCE_DIR.git"
+git clone --branch="$TARGET_BRANCH" "https://github.com/$TARGET_REPO_SLUG.git" "$GIT_DIR"
+
+# setup git
+cd "$GIT_DIR"
+git config user.name "Travis CI"
+git config user.email "travis-ci@picocms.org"
+
+if [ -n "$GITHUB_OAUTH_TOKEN" ]; then
+ git config credential.helper 'store --file=.git/credentials'
+ (umask 077 && echo "https://GitHub:$GITHUB_OAUTH_TOKEN@github.com" > .git/credentials)
+fi
+
+# copy phpdoc
+printf '\nCopying phpDocs...\n'
+[ ! -d "$TARGET_DIR" ] || rm -rf "$TARGET_DIR"
+[ "${SOURCE_DIR:0:1}" == "/" ] || SOURCE_DIR="$BASE_PWD/$SOURCE_DIR"
+cp -R "$SOURCE_DIR" "$TARGET_DIR"
+
+# commit changes
+printf '\nCommiting changes...\n'
+git add --all "$TARGET_DIR"
+git commit --message="Update phpDocumentor class docs for $SOURCE_REF"
+
+# very simple race condition protection for concurrent Travis builds
+# this is no definite protection (race conditions are still possible during `git push`),
+# but it should give a basic protection without disabling concurrent builds completely
+if [ "$SOURCE_REF_TYPE" == "commit" ]; then
+ # load branch data via GitHub APIv3
+ printf '\nRetrieving latest commit...\n'
+ LATEST_COMMIT_URL="https://api.github.com/repos/$SOURCE_REPO_SLUG/git/refs/$SOURCE_REF_HEAD"
+ if [ -n "$GITHUB_OAUTH_TOKEN" ]; then
+ LATEST_COMMIT_RESPONSE="$(wget -O- --header="Authorization: token $GITHUB_OAUTH_TOKEN" "$LATEST_COMMIT_URL" 2> /dev/null)"
+ else
+ LATEST_COMMIT_RESPONSE="$(wget -O- "$LATEST_COMMIT_URL" 2> /dev/null)"
+ fi
+
+ # evaluate JSON response
+ LATEST_COMMIT="$(echo "$LATEST_COMMIT_RESPONSE" | php -r "
+ \$json = json_decode(stream_get_contents(STDIN), true);
+ if (\$json !== null) {
+ if (isset(\$json['ref']) && (\$json['ref'] === 'refs/$SOURCE_REF_HEAD')) {
+ if (isset(\$json['object']) && isset(\$json['object']['sha'])) {
+ echo \$json['object']['sha'];
+ }
+ }
+ }
+ ")"
+
+ # compare source reference against the latest commit
+ if [ "$LATEST_COMMIT" != "$SOURCE_REF_COMMIT" ]; then
+ echo "WARNING: $APP_NAME source reference '$SOURCE_REF' doesn't match the latest commit '$LATEST_COMMIT'" >&2
+ exit 0
+ fi
+fi
+
+# push changes
+printf '\nPushing changes...\n'
+git push "https://github.com/$TARGET_REPO_SLUG.git" "$TARGET_BRANCH:$TARGET_BRANCH"
+
+echo
diff --git a/build/generate-phpdoc.sh b/build/generate-phpdoc.sh
new file mode 100755
index 0000000..c2fe334
--- /dev/null
+++ b/build/generate-phpdoc.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+set -e
+
+# parameters
+PHPDOC_SOURCE_DIR="$1"
+PHPDOC_TARGET_DIR="$2"
+PHPDOC_TITLE="$3"
+
+# print parameters
+echo "Generating phpDocs..."
+printf 'PHPDOC_SOURCE_DIR="%s"\n' "$PHPDOC_SOURCE_DIR"
+printf 'PHPDOC_TARGET_DIR="%s"\n' "$PHPDOC_TARGET_DIR"
+printf 'PHPDOC_TITLE="%s"\n' "$PHPDOC_TITLE"
+echo
+
+# generate phpdoc
+phpdoc -d "$PHPDOC_SOURCE_DIR" \
+ -i "$PHPDOC_SOURCE_DIR/build/" \
+ -i "$PHPDOC_SOURCE_DIR/vendor/" \
+ -i "$PHPDOC_SOURCE_DIR/plugins/" -f "$PHPDOC_SOURCE_DIR/plugins/DummyPlugin.php" \
+ -t "$PHPDOC_TARGET_DIR" \
+ --title "$PHPDOC_TITLE"
+
+echo
diff --git a/composer.json b/composer.json
index b0d75e1..5894970 100644
--- a/composer.json
+++ b/composer.json
@@ -2,20 +2,35 @@
"name": "picocms/pico",
"type": "library",
"description": "Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create .md files in the \"content\" folder and that becomes a page.",
- "keywords": ["cms"],
+ "keywords": ["flat-file","cms","php","twig","markdown"],
"homepage": "http://picocms.org/",
"license": "MIT",
"authors": [
{
"name": "Gilbert Pellegrom",
- "email": "gilbert@pellegrom.me"
+ "email": "gilbert@pellegrom.me",
+ "role": "Project Founder"
+ },
+ {
+ "name": "The Pico Community",
+ "homepage": "https://github.com/picocms/Pico/graphs/contributors",
+ "role": "Contributors"
}
],
+ "support": {
+ "docs": "http://picocms.org/docs",
+ "issues": "https://github.com/picocms/Pico/issues",
+ "source": "https://github.com/picocms/Pico"
+ },
"require": {
"php": ">=5.3.6",
- "twig/twig": "1.18.*",
- "erusev/parsedown-extra": "0.7.*",
- "symfony/yaml" : "2.3"
+ "twig/twig": "^1.18",
+ "erusev/parsedown-extra": "^0.7",
+ "symfony/yaml" : "^2.3"
+ },
+ "require-dev" : {
+ "phpdocumentor/phpdocumentor": "^2.8",
+ "squizlabs/php_codesniffer": "^2.4"
},
"autoload": {
"psr-0": {
diff --git a/content-sample/index.md b/content-sample/index.md
index 389a09a..218646a 100644
--- a/content-sample/index.md
+++ b/content-sample/index.md
@@ -62,12 +62,18 @@ Instead of adding your own content to the `content-sample` folder, you should
create your own `content` directory in Pico's root directory. You can then add
and access your contents as described above.
+As a common practice, we recommend you to separate your contents and assets
+(like images, downloads etc.). We even deny access to your `content` directory
+by default. So if you want to use a asset (e.g. a image) in one of your content
+files, upload it to the (to be created) directory `assets` and use it as
+follows: !\[Image Title\](%base_url%/assets/image.png)
+
### Text File Markup
Text files are marked up using [Markdown][]. They can also contain regular HTML.
-At the top of text files you can place a block comment and specify certain
-attributes of the page. For example:
+At the top of text files you can place a block comment and specify certain meta
+attributes of the page using [YAML][] (the "YAML header"). For example:
---
Title: Welcome
@@ -290,6 +296,7 @@ setting `$config['rewrite_url'] = true;` in your `config/config.php`.
For more help have a look at the Pico documentation at http://picocms.org/docs.
[Markdown]: http://daringfireball.net/projects/markdown/syntax
+[YAML]: https://en.wikipedia.org/wiki/YAML
[Twig]: http://twig.sensiolabs.org/documentation
[WikiThemes]: https://github.com/picocms/Pico/wiki/Pico-Themes
[WikiPlugins]: https://github.com/picocms/Pico/wiki/Pico-Plugins
diff --git a/index.php b/index.php
index 40b811a..7e7cdf0 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,15 @@
-setConfig(array());
+//$pico->setConfig(array());
// run application
echo $pico->run();
diff --git a/index.php.dist b/index.php.dist
new file mode 100644
index 0000000..2186072
--- /dev/null
+++ b/index.php.dist
@@ -0,0 +1,20 @@
+run();
diff --git a/lib/Pico.php b/lib/Pico.php
index 42454e2..334d792 100644
--- a/lib/Pico.php
+++ b/lib/Pico.php
@@ -266,7 +266,8 @@ class Pico
* meta headers, processes Markdown, does Twig processing and returns
* the rendered contents.
*
- * @return string rendered Pico contents
+ * @return string rendered Pico contents
+ * @throws RuntimeException thrown when a not recoverable error occurs
*/
public function run()
{
@@ -281,6 +282,11 @@ class Pico
$this->loadConfig();
$this->triggerEvent('onConfigLoaded', array(&$this->config));
+ // check content dir
+ if (!is_dir($this->getConfig('content_dir'))) {
+ throw new RuntimeException('Invalid content directory "' . $this->getConfig('content_dir') . '"');
+ }
+
// evaluate request url
$this->evaluateRequestUrl();
$this->triggerEvent('onRequestUrl', array(&$this->requestUrl));
@@ -446,6 +452,10 @@ class Pico
protected function loadConfig()
{
$config = null;
+ if (file_exists($this->getConfigDir() . 'config.php')) {
+ require($this->getConfigDir() . 'config.php');
+ }
+
$defaultConfig = array(
'site_title' => 'Pico',
'base_url' => '',
@@ -460,11 +470,6 @@ class Pico
'timezone' => ''
);
- $configFile = $this->getConfigDir() . 'config.php';
- if (file_exists($configFile)) {
- require $configFile;
- }
-
$this->config = is_array($this->config) ? $this->config : array();
$this->config += is_array($config) ? $config + $defaultConfig : $defaultConfig;
@@ -474,6 +479,10 @@ class Pico
$this->config['base_url'] = rtrim($this->config['base_url'], '/') . '/';
}
+ if ($this->config['rewrite_url'] === null) {
+ $this->config['rewrite_url'] = $this->isUrlRewritingEnabled();
+ }
+
if (empty($this->config['content_dir'])) {
// try to guess the content directory
if (is_dir($this->getRootDir() . 'content')) {
@@ -558,9 +567,9 @@ class Pico
*
* We recommend you to use the `link` filter in templates to create
* internal links, e.g. `{{ "sub/page"|link }}` is equivalent to
- * `{{ base_url }}sub/page`. In content files you can still use the
- * `%base_url%` variable; e.g. `%base_url%?sub/page` will be automatically
- * replaced accordingly.
+ * `{{ base_url }}/sub/page` and `{{ base_url }}?sub/page`, depending on
+ * enabled URL rewriting. In content files you can use the `%base_url%`
+ * variable; e.g. `%base_url%?sub/page` will be replaced accordingly.
*
* @see Pico::getRequestUrl()
* @return void
@@ -578,6 +587,7 @@ class Pico
$pathComponent = substr($pathComponent, 0, $pathComponentLength);
}
$this->requestUrl = (strpos($pathComponent, '=') === false) ? rawurldecode($pathComponent) : '';
+ $this->requestUrl = trim($this->requestUrl, '/');
}
/**
@@ -762,7 +772,7 @@ class Pico
$meta[$fieldId] = $meta[$fieldName];
unset($meta[$fieldName]);
}
- } else {
+ } elseif (!isset($meta[$fieldId])) {
// guarantee array key existance
$meta[$fieldId] = '';
}
@@ -776,10 +786,7 @@ class Pico
}
} else {
// guarantee array key existance
- foreach ($headers as $id => $field) {
- $meta[$id] = '';
- }
-
+ $meta = array_fill_keys(array_keys($headers), '');
$meta['time'] = $meta['date_formatted'] = '';
}
@@ -929,7 +936,7 @@ class Pico
$files = $this->getFiles($this->getConfig('content_dir'), $this->getConfig('content_ext'), Pico::SORT_NONE);
foreach ($files as $i => $file) {
// skip 404 page
- if (basename($file) == '404' . $this->getConfig('content_ext')) {
+ if (basename($file) === '404' . $this->getConfig('content_ext')) {
unset($files[$i]);
continue;
}
@@ -967,7 +974,7 @@ class Pico
'meta' => &$meta
);
- if ($file == $this->requestFile) {
+ if ($file === $this->requestFile) {
$page['content'] = &$this->content;
}
@@ -996,10 +1003,10 @@ class Pico
$bSortKey = (basename($b['id']) === 'index') ? dirname($b['id']) : $b['id'];
$cmp = strcmp($aSortKey, $bSortKey);
- return $cmp * (($order == 'desc') ? -1 : 1);
+ return $cmp * (($order === 'desc') ? -1 : 1);
};
- if ($this->getConfig('pages_order_by') == 'date') {
+ if ($this->getConfig('pages_order_by') === 'date') {
// sort by date
uasort($this->pages, function ($a, $b) use ($alphaSortClosure, $order) {
if (empty($a['time']) || empty($b['time'])) {
@@ -1013,7 +1020,7 @@ class Pico
return $alphaSortClosure($a, $b);
}
- return $cmp * (($order == 'desc') ? 1 : -1);
+ return $cmp * (($order === 'desc') ? 1 : -1);
});
} else {
// sort alphabetically
@@ -1026,7 +1033,7 @@ class Pico
*
* @see Pico::readPages()
* @see Pico::sortPages()
- * @return array|null the data of all pages
+ * @return array[]|null the data of all pages
*/
public function getPages()
{
@@ -1053,7 +1060,7 @@ class Pico
if ($currentPageIndex !== false) {
$this->currentPage = &$this->pages[$currentPageId];
- if (($this->getConfig('order_by') == 'date') && ($this->getConfig('order') == 'desc')) {
+ if (($this->getConfig('order_by') === 'date') && ($this->getConfig('order') === 'desc')) {
$previousPageOffset = 1;
$nextPageOffset = -1;
} else {
@@ -1179,7 +1186,7 @@ class Pico
'prev_page' => $this->previousPage,
'current_page' => $this->currentPage,
'next_page' => $this->nextPage,
- 'is_front_page' => ($this->requestFile == $frontPage),
+ 'is_front_page' => ($this->requestFile === $frontPage),
);
}
@@ -1195,19 +1202,18 @@ class Pico
return $baseUrl;
}
- if (
- (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
- || ($_SERVER['SERVER_PORT'] == 443)
- || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
- ) {
+ $protocol = 'http';
+ if (!empty($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] !== 'off')) {
+ $protocol = 'https';
+ } elseif ($_SERVER['SERVER_PORT'] == 443) {
+ $protocol = 'https';
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) {
$protocol = 'https';
- } else {
- $protocol = 'http';
}
$this->config['base_url'] =
$protocol . "://" . $_SERVER['HTTP_HOST']
- . dirname($_SERVER['SCRIPT_NAME']) . '/';
+ . rtrim(dirname($_SERVER['SCRIPT_NAME']), '/') . '/';
return $this->getConfig('base_url');
}
@@ -1219,13 +1225,13 @@ class Pico
*/
public function isUrlRewritingEnabled()
{
- if (($this->getConfig('rewrite_url') === null) && isset($_SERVER['PICO_URL_REWRITING'])) {
- return (bool) $_SERVER['PICO_URL_REWRITING'];
- } elseif ($this->getConfig('rewrite_url')) {
- return true;
+ $urlRewritingEnabled = $this->getConfig('rewrite_url');
+ if ($urlRewritingEnabled !== null) {
+ return $urlRewritingEnabled;
}
- return false;
+ $this->config['rewrite_url'] = (isset($_SERVER['PICO_URL_REWRITING']) && $_SERVER['PICO_URL_REWRITING']);
+ return $this->getConfig('rewrite_url');
}
/**
@@ -1294,7 +1300,7 @@ class Pico
* @param string $path relative or absolute path
* @return string absolute path
*/
- protected function getAbsolutePath($path)
+ public function getAbsolutePath($path)
{
if (substr($path, 0, 1) !== '/') {
$path = $this->getRootDir() . $path;
@@ -1320,8 +1326,7 @@ class Pico
if (!empty($this->plugins)) {
foreach ($this->plugins as $plugin) {
// only trigger events for plugins that implement PicoPluginInterface
- // deprecated events (plugins for Pico 0.9 and older) will be
- // triggered by the `PicoPluginDeprecated` plugin
+ // deprecated events (plugins for Pico 0.9 and older) will be triggered by `PicoDeprecated`
if (is_a($plugin, 'PicoPluginInterface')) {
$plugin->handleEvent($eventName, $params);
}
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..93b9ebb
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,39 @@
+
+
+
+ Pico's coding standards mainly base on the PHP-FIG PSR-2 standard,
+ but without the MissingNamespace sniff.
+
+
+
+ ^build/
+ ^vendor/
+ *.min.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/00-PicoDeprecated.php b/plugins/00-PicoDeprecated.php
index 53bfff2..5240160 100644
--- a/plugins/00-PicoDeprecated.php
+++ b/plugins/00-PicoDeprecated.php
@@ -67,7 +67,7 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onPluginsLoaded()
*/
- public function onPluginsLoaded(&$plugins)
+ public function onPluginsLoaded(array &$plugins)
{
if (!empty($plugins)) {
foreach ($plugins as $plugin) {
@@ -107,19 +107,17 @@ class PicoDeprecated extends AbstractPicoPlugin
* @see PicoDeprecated::loadRootDirConfig()
* @see PicoDeprecated::enablePlugins()
* @see DummyPlugin::onConfigLoaded()
- * @param mixed[] &$realConfig array of config variables
+ * @param mixed[] &$config array of config variables
* @return void
*/
- public function onConfigLoaded(&$realConfig)
+ public function onConfigLoaded(array &$config)
{
- global $config;
-
$this->defineConstants();
- $this->loadRootDirConfig($realConfig);
+ $this->loadRootDirConfig($config);
$this->enablePlugins();
- $config = &$realConfig;
+ $GLOBALS['config'] = &$config;
- $this->triggerEvent('config_loaded', array(&$realConfig));
+ $this->triggerEvent('config_loaded', array(&$config));
}
/**
@@ -167,14 +165,22 @@ class PicoDeprecated extends AbstractPicoPlugin
* @param mixed[] &$realConfig array of config variables
* @return void
*/
- protected function loadRootDirConfig(&$realConfig)
+ protected function loadRootDirConfig(array &$realConfig)
{
if (file_exists($this->getRootDir() . 'config.php')) {
- // config.php in Pico::$rootDir is deprecated; use Pico::$configDir instead
+ // config.php in Pico::$rootDir is deprecated
+ // use config.php in Pico::$configDir instead
$config = null;
require($this->getRootDir() . 'config.php');
if (is_array($config)) {
+ if (isset($config['base_url'])) {
+ $config['base_url'] = rtrim($config['base_url'], '/') . '/';
+ }
+ if (isset($config['content_dir'])) {
+ $config['content_dir'] = rtrim($config['content_dir'], '/') . '/';
+ }
+
$realConfig = $config + $realConfig;
}
}
@@ -276,7 +282,7 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onMetaHeaders()
*/
- public function onMetaHeaders(&$headers)
+ public function onMetaHeaders(array &$headers)
{
$this->triggerEvent('before_read_file_meta', array(&$headers));
}
@@ -286,7 +292,7 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onMetaParsed()
*/
- public function onMetaParsed(&$meta)
+ public function onMetaParsed(array &$meta)
{
$this->triggerEvent('file_meta', array(&$meta));
}
@@ -320,7 +326,7 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onSinglePageLoaded()
*/
- public function onSinglePageLoaded(&$pageData)
+ public function onSinglePageLoaded(array &$pageData)
{
$this->triggerEvent('get_page_data', array(&$pageData, $pageData['meta']));
}
@@ -336,8 +342,12 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onPagesLoaded()
*/
- public function onPagesLoaded(&$pages, &$currentPage, &$previousPage, &$nextPage)
- {
+ public function onPagesLoaded(
+ array &$pages,
+ array &$currentPage = null,
+ array &$previousPage = null,
+ array &$nextPage = null
+ ) {
// remove keys of pages array
$plainPages = array();
foreach ($pages as &$pageData) {
@@ -383,7 +393,7 @@ class PicoDeprecated extends AbstractPicoPlugin
*
* @see DummyPlugin::onPageRendering()
*/
- public function onPageRendering(&$twig, &$twigVariables, &$templateName)
+ public function onPageRendering(Twig_Environment &$twig, array &$twigVariables, &$templateName)
{
// template name contains file extension since Pico 1.0
$fileExtension = '';
diff --git a/plugins/01-PicoParsePagesContent.php b/plugins/01-PicoParsePagesContent.php
index fd36f0a..db4ed10 100644
--- a/plugins/01-PicoParsePagesContent.php
+++ b/plugins/01-PicoParsePagesContent.php
@@ -30,7 +30,7 @@ class PicoParsePagesContent extends AbstractPicoPlugin
*
* @see DummyPlugin::onSinglePageLoaded()
*/
- public function onSinglePageLoaded(&$pageData)
+ public function onSinglePageLoaded(array &$pageData)
{
if (!isset($pageData['content'])) {
$pageData['content'] = $this->prepareFileContent($pageData['raw_content'], $pageData['meta']);
diff --git a/plugins/02-PicoExcerpt.php b/plugins/02-PicoExcerpt.php
index 4ada382..7d0c449 100644
--- a/plugins/02-PicoExcerpt.php
+++ b/plugins/02-PicoExcerpt.php
@@ -40,7 +40,7 @@ class PicoExcerpt extends AbstractPicoPlugin
*
* @see DummyPlugin::onConfigLoaded()
*/
- public function onConfigLoaded(&$config)
+ public function onConfigLoaded(array &$config)
{
if (!isset($config['excerpt_length'])) {
$config['excerpt_length'] = 50;
@@ -53,7 +53,7 @@ class PicoExcerpt extends AbstractPicoPlugin
* @see PicoExcerpt::createExcerpt()
* @see DummyPlugin::onSinglePageLoaded()
*/
- public function onSinglePageLoaded(&$pageData)
+ public function onSinglePageLoaded(array &$pageData)
{
if (!isset($pageData['excerpt'])) {
$pageData['excerpt'] = $this->createExcerpt(
diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php
index 9ecd411..f8ebb01 100644
--- a/plugins/DummyPlugin.php
+++ b/plugins/DummyPlugin.php
@@ -11,7 +11,7 @@
* @license http://opensource.org/licenses/MIT
* @version 1.0
*/
-class DummyPlugin extends AbstractPicoPlugin
+final class DummyPlugin extends AbstractPicoPlugin
{
/**
* This plugin is enabled by default?
@@ -40,7 +40,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param object[] &$plugins loaded plugin instances
* @return void
*/
- public function onPluginsLoaded(&$plugins)
+ public function onPluginsLoaded(array &$plugins)
{
// your code
}
@@ -52,7 +52,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param mixed[] &$config array of config variables
* @return void
*/
- public function onConfigLoaded(&$config)
+ public function onConfigLoaded(array &$config)
{
// your code
}
@@ -141,7 +141,7 @@ class DummyPlugin extends AbstractPicoPlugin
* array key is later used to access the found value
* @return void
*/
- public function onMetaHeaders(&$headers)
+ public function onMetaHeaders(array &$headers)
{
// your code
}
@@ -155,7 +155,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param string[] &$headers known meta header fields
* @return void
*/
- public function onMetaParsing(&$rawContent, &$headers)
+ public function onMetaParsing(&$rawContent, array &$headers)
{
// your code
}
@@ -167,7 +167,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param string[] &$meta parsed meta data
* @return void
*/
- public function onMetaParsed(&$meta)
+ public function onMetaParsed(array &$meta)
{
// your code
}
@@ -249,7 +249,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param array &$pageData data of the loaded page
* @return void
*/
- public function onSinglePageLoaded(&$pageData)
+ public function onSinglePageLoaded(array &$pageData)
{
// your code
}
@@ -264,14 +264,18 @@ class DummyPlugin extends AbstractPicoPlugin
* @see Pico::getCurrentPage()
* @see Pico::getPreviousPage()
* @see Pico::getNextPage()
- * @param array &$pages data of all known pages
- * @param array &$currentPage data of the page being served
- * @param array &$previousPage data of the previous page
- * @param array &$nextPage data of the next page
+ * @param array[] &$pages data of all known pages
+ * @param array|null &$currentPage data of the page being served
+ * @param array|null &$previousPage data of the previous page
+ * @param array|null &$nextPage data of the next page
* @return void
*/
- public function onPagesLoaded(&$pages, &$currentPage, &$previousPage, &$nextPage)
- {
+ public function onPagesLoaded(
+ array &$pages,
+ array &$currentPage = null,
+ array &$previousPage = null,
+ array &$nextPage = null
+ ) {
// your code
}
@@ -295,7 +299,7 @@ class DummyPlugin extends AbstractPicoPlugin
* @param string &$templateName file name of the template
* @return void
*/
- public function onPageRendering(&$twig, &$twigVariables, &$templateName)
+ public function onPageRendering(Twig_Environment &$twig, array &$twigVariables, &$templateName)
{
// your code
}
diff --git a/themes/default/style.css b/themes/default/style.css
index b1f3ca9..d26f65a 100644
--- a/themes/default/style.css
+++ b/themes/default/style.css
@@ -1,359 +1,367 @@
-/*=================================*/
-/* Pico Default Theme
-/* By: Gilbert Pellegrom
-/* http: //dev7studios.com
-/*=================================*/
-
-/* Reset Styles
-/*---------------------------------------------*/
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, font, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- font-weight: inherit;
- font-style: inherit;
- font-size: 100%;
- font-family: inherit;
- vertical-align: baseline;
-}
-
-body {
- line-height: 1;
- color: black;
- background: white;
-}
-
-table {
- border-collapse: separate;
- border-spacing: 0;
-}
-
-caption, th, td {
- text-align: left;
- font-weight: normal;
-}
-
-blockquote:before, blockquote:after,
-q:before, q:after {
- content: "";
-}
-
-blockquote, q {
- quotes: "" "";
-}
-
-/* HTML5 tags */
-header, section, footer,
-aside, nav, article, figure {
- display: block;
-}
-
-/* hand cursor on clickable input elements */
-label, input[type=button], input[type=submit], button {
- cursor: pointer;
-}
-
-/* make buttons play nice in IE:
- www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */
-button {
- width: auto;
- overflow: visible;
-}
-
-/* Sharper Thumbnails */
-img {
- -ms-interpolation-mode: bicubic;
-}
-
-/* Input Styles
-/*---------------------------------------------*/
-input,
-textarea,
-select {
- padding: 5px;
- font: 400 1em Verdana, Sans-serif;
- color: #666;
- background: #fff;
- border: 1px solid #999999;
- margin: 0 0 1em 0;
-}
-
-input:focus,
-textarea:focus,
-select:focus {
- color: #000;
- background: #fff;
- border: 1px solid #666666;
-}
-
-/* Main Styles
-/*---------------------------------------------*/
-body {
- font: 14px/1.8em 'Open Sans', Helvetica, Arial, Helvetica, sans-serif;
- color: #444;
- background: #fff;
- -webkit-font-smoothing: antialiased;
-}
-
-a, a:visited {
- color: #2EAE9B;
- text-decoration: none;
- -webkit-transition: all 0.2s linear;
- -moz-transition: all 0.2s linear;
- -ms-transition: all 0.2s linear;
- -o-transition: all 0.2s linear;
- transition: all 0.2s linear;
-}
-
-a:hover, a:active {
- color: #000;
- text-decoration: none;
-}
-
-h1, h2, h3, h4, h5, h6 {
- color: #000;
- line-height: 1.2em;
- margin-bottom: 0.6em;
-}
-
-h1 {
- font-size: 2em;
-}
-
-h2 {
- font-size: 1.7em;
-}
-
-h3 {
- font-size: 1.5em;
- margin-top: 2em;
-}
-
-p {
- margin-bottom: 1em;
-}
-
-ol, ul {
- padding-left: 30px;
- margin-bottom: 1em;
-}
-
-b, strong {
- font-weight: bold;
-}
-
-i, em {
- font-style: italic;
-}
-
-u {
- text-decoration: underline;
-}
-
-abbr, acronym {
- cursor: help;
- border-bottom: 0.1em dotted;
-}
-
-td, td img {
- vertical-align: top;
-}
-
-sub {
- vertical-align: sub;
- font-size: smaller;
-}
-
-sup {
- vertical-align: super;
- font-size: smaller;
-}
-
-code {
- font-family: Courier, "Courier New", Monaco, Tahoma;
- background: #eee;
- color: #333;
- padding: 0px 2px;
-}
-
-pre {
- background: #eee;
- padding: 20px;
- margin-bottom: 1em;
- overflow: auto;
-}
-
-blockquote {
- font-style: italic;
- margin: 0 0 1em 15px;
- padding-left: 10px;
- border-left: 5px solid #dddddd;
-}
-
-/* Structure Styles
-/*---------------------------------------------*/
-.inner {
- width: 850px;
- margin: 0 auto;
-}
-
-#header {
- background: #2EAE9B;
- padding: 60px 0;
- margin-bottom: 80px;
- color: #afe1da;
-}
-#header a { color: #afe1da; }
-#header h1 a,
-#header a:hover { color: #fff; }
-#header h1 {
- font-weight: bold;
- margin: 0;
- float: left;
-}
-#header .menu-icon {
- display: none;
- width: 35px;
- height: 35px;
- background: #afe1da url(menu-icon.png) center;
-}
-#header nav {
- float: right;
- list-style: none;
- margin: 0;
- padding: 0;
-}
-#header nav a {
- font-weight: bold;
- margin-left: 20px;
-}
-#header a:hover#menu-icon {
- background-color: #444;
- border-radius: 4px 4px 0 0;
-}
-#header ul {
- list-style: none;
-}
-#header li {
- display: inline-block;
- float: left;
-}
-#footer {
- background: #707070;
- padding: 60px 0;
- margin-top: 80px;
- color: #C0C0C0;
-}
-#footer a { color: #ddd; }
-#footer a:hover { color: #fff; }
-
-/* Misc Styles
-/*---------------------------------------------*/
-.clearfix:before,
-.clearfix:after {
- content: " ";
- display: table;
-}
-.clearfix:after {
- clear: both;
-}
-.clearfix {
- *zoom: 1;
-}
-
-/* Media Queries
-/*---------------------------------------------*/
-
-/* Small Devices, Tablets */
-@media only screen and (max-width : 768px) {
-
- .inner {
- width: 85%;
- }
- .inner img {
- width:100%;
- }
- #header {
- margin-bottom: 40px;
- }
- #header h1 a {
- font-size:1em;
- }
- #header .menu-icon {
- display:inline-block;
- }
- #header nav a { color: #000; }
- #header nav a:hover { color: #afe1da; }
- #header nav ul, nav:active ul {
- display: none;
- position: absolute;
- padding: 20px;
- background: #fff;
- border: 5px solid #444;
- right: 2.7em;
- top: 100px;
- width: 80%;
- border-radius: 4px 0 4px 4px;
- z-index: 9999;
- }
- #header nav li {
- text-align: center;
- width: 100%;
- padding: 10px 0;
- margin: 0;
- }
- #header nav:hover ul {
- display: block;
- }
-
-}
-
-/* Extra Small Devices, Phones */
-@media only screen and (max-width : 480px) {
-
- .inner {
- width: 85%;
- }
- .inner img {
- width:100%;
- }
- #header {
- margin-bottom: 30px;
- }
- #header h1 a {
- font-size:1em;
- }
- #header .menu-icon {
- display:inline-block;
- }
- #header nav a { color: #000; }
- #header nav a:hover { color: #afe1da; }
- #header nav ul, nav:active ul {
- display: none;
- position: absolute;
- padding: 20px;
- background: #fff;
- border: 5px solid #444;
- right: 0em;
- top: 100px;
- width: auto;
- border-radius: 4px 0 4px 4px;
- }
- #header nav li {
- text-align: center;
- width: 100%;
- padding: 10px 0;
- margin: 0;
- }
- #header nav:hover ul {
- display: block;
- }
-}
\ No newline at end of file
+/*=================================*/
+/* Pico Default Theme
+/* By: Gilbert Pellegrom
+/* http: //dev7studios.com
+/*=================================*/
+
+/* Reset Styles
+/*---------------------------------------------*/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+
+body {
+ line-height: 1;
+ color: black;
+ background: white;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: "";
+}
+
+blockquote, q {
+ quotes: "" "";
+}
+
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+ display: block;
+}
+
+/* hand cursor on clickable input elements */
+label, input[type=button], input[type=submit], button {
+ cursor: pointer;
+}
+
+/* make buttons play nice in IE:
+ www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */
+button {
+ width: auto;
+ overflow: visible;
+}
+
+/* Sharper Thumbnails */
+img {
+ -ms-interpolation-mode: bicubic;
+}
+
+/* Input Styles
+/*---------------------------------------------*/
+input,
+textarea,
+select {
+ padding: 5px;
+ font: 400 1em Verdana, Sans-serif;
+ color: #666;
+ background: #fff;
+ border: 1px solid #999999;
+ margin: 0 0 1em 0;
+}
+
+input:focus,
+textarea:focus,
+select:focus {
+ color: #000;
+ background: #fff;
+ border: 1px solid #666666;
+}
+
+/* Main Styles
+/*---------------------------------------------*/
+body {
+ font: 14px/1.8em 'Open Sans', Helvetica, Arial, Helvetica, sans-serif;
+ color: #444;
+ background: #fff;
+ -webkit-font-smoothing: antialiased;
+}
+
+a, a:visited {
+ color: #2EAE9B;
+ text-decoration: none;
+ -webkit-transition: all 0.2s linear;
+ -moz-transition: all 0.2s linear;
+ -ms-transition: all 0.2s linear;
+ -o-transition: all 0.2s linear;
+ transition: all 0.2s linear;
+}
+
+a:hover, a:active {
+ color: #000;
+ text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #000;
+ line-height: 1.2em;
+ margin-bottom: 0.6em;
+}
+
+h1 {
+ font-size: 2em;
+}
+
+h2 {
+ font-size: 1.7em;
+}
+
+h3 {
+ font-size: 1.5em;
+ margin-top: 2em;
+}
+
+p, table {
+ margin-bottom: 1em;
+}
+
+ol, ul {
+ padding-left: 30px;
+ margin-bottom: 1em;
+}
+
+b, strong {
+ font-weight: bold;
+}
+
+i, em {
+ font-style: italic;
+}
+
+u {
+ text-decoration: underline;
+}
+
+abbr, acronym {
+ cursor: help;
+ border-bottom: 0.1em dotted;
+}
+
+td, td img {
+ vertical-align: top;
+}
+
+td, th {
+ border: solid 1px #999;
+ padding: 0.25em 0.5em;
+}
+
+th {
+ font-weight: bold;
+ text-align: center;
+ background: #eee;
+}
+
+sub {
+ vertical-align: sub;
+ font-size: smaller;
+}
+
+sup {
+ vertical-align: super;
+ font-size: smaller;
+}
+
+code {
+ font-family: Courier, "Courier New", Monaco, Tahoma;
+ background: #eee;
+ color: #333;
+ padding: 0px 2px;
+}
+
+pre {
+ background: #eee;
+ padding: 20px;
+ margin-bottom: 1em;
+ overflow: auto;
+}
+
+blockquote {
+ font-style: italic;
+ margin: 0 0 1em 15px;
+ padding-left: 10px;
+ border-left: 5px solid #dddddd;
+}
+
+/* Structure Styles
+/*---------------------------------------------*/
+.inner {
+ width: 850px;
+ margin: 0 auto;
+}
+
+#header {
+ background: #2EAE9B;
+ padding: 60px 0;
+ margin-bottom: 80px;
+ color: #afe1da;
+}
+#header a { color: #afe1da; }
+#header h1 a,
+#header a:hover { color: #fff; }
+#header h1 {
+ font-weight: bold;
+ margin: 0;
+ float: left;
+}
+#header .menu-icon {
+ display: none;
+ width: 35px;
+ height: 35px;
+ background: #afe1da url(menu-icon.png) center;
+}
+#header nav {
+ float: right;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+#header nav a {
+ font-weight: bold;
+ margin-left: 20px;
+}
+#header a:hover#menu-icon {
+ background-color: #444;
+ border-radius: 4px 4px 0 0;
+}
+#header ul {
+ list-style: none;
+}
+#header li {
+ display: inline-block;
+ float: left;
+}
+#footer {
+ background: #707070;
+ padding: 60px 0;
+ margin-top: 80px;
+ color: #C0C0C0;
+}
+#footer a { color: #ddd; }
+#footer a:hover { color: #fff; }
+
+/* Misc Styles
+/*---------------------------------------------*/
+.clearfix:before,
+.clearfix:after {
+ content: " ";
+ display: table;
+}
+.clearfix:after {
+ clear: both;
+}
+.clearfix {
+ *zoom: 1;
+}
+
+/* Media Queries
+/*---------------------------------------------*/
+
+/* Small Devices, Tablets */
+@media only screen and (max-width : 768px) {
+ .inner {
+ width: 85%;
+ }
+ .inner img {
+ width:100%;
+ }
+ #header {
+ margin-bottom: 40px;
+ }
+ #header h1 a {
+ font-size:1em;
+ }
+ #header .menu-icon {
+ display:inline-block;
+ }
+ #header nav a { color: #000; }
+ #header nav a:hover { color: #afe1da; }
+ #header nav ul, nav:active ul {
+ display: none;
+ position: absolute;
+ padding: 20px;
+ background: #fff;
+ border: 5px solid #444;
+ right: 2.7em;
+ top: 100px;
+ width: 80%;
+ border-radius: 4px 0 4px 4px;
+ z-index: 9999;
+ }
+ #header nav li {
+ text-align: center;
+ width: 100%;
+ padding: 10px 0;
+ margin: 0;
+ }
+ #header nav:hover ul {
+ display: block;
+ }
+}
+
+/* Extra Small Devices, Phones */
+@media only screen and (max-width : 480px) {
+ .inner {
+ width: 85%;
+ }
+ .inner img {
+ width:100%;
+ }
+ #header {
+ margin-bottom: 30px;
+ }
+ #header h1 a {
+ font-size:1em;
+ }
+ #header .menu-icon {
+ display:inline-block;
+ }
+ #header nav a { color: #000; }
+ #header nav a:hover { color: #afe1da; }
+ #header nav ul, nav:active ul {
+ display: none;
+ position: absolute;
+ padding: 20px;
+ background: #fff;
+ border: 5px solid #444;
+ right: 0em;
+ top: 100px;
+ width: auto;
+ border-radius: 4px 0 4px 4px;
+ }
+ #header nav li {
+ text-align: center;
+ width: 100%;
+ padding: 10px 0;
+ margin: 0;
+ }
+ #header nav:hover ul {
+ display: block;
+ }
+}