diff --git a/.gitignore b/.gitignore index bb580b7..153216e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ +.DS_Store node_modules/ -vendor -composer.lock -www/charcoal -www/modules/boilerplate/npm-debug.log +npm-debug.log diff --git a/Gruntfile.js b/Gruntfile.js index b68367f..cd06ab0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,5 @@ module.exports = function(grunt) { - function loadConfig(path) { var glob = require('glob'); var object = {}; @@ -16,7 +15,8 @@ module.exports = function(grunt) { var config = { pkg: grunt.file.readJSON('package.json') - } + }; + grunt.loadTasks('grunt_tasks'); grunt.util._.extend(config, loadConfig('./grunt_tasks/')); grunt.initConfig(config); @@ -24,7 +24,6 @@ module.exports = function(grunt) { // Load tasks require('load-grunt-tasks')(grunt); - // Register tasks grunt.registerTask('default', ['watch', 'notify:watch']); grunt.registerTask('sync', ['browserSync', 'watch', 'notify:watch']); @@ -44,12 +43,4 @@ module.exports = function(grunt) { grunt.registerTask('j', [ 'jshint' ]); - grunt.registerTask('wlint', [ - // PHP - 'phplint', - 'jsonlint', - // Utilities - 'watch' - ]); - }; diff --git a/README.md b/README.md index 845c86c..2b499a5 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,18 @@ -Charcoal-Boilerplate +Locomotive front-end boilerplate ==================== -Boilerplate for [`Charcoal`](#http://charcoal.locomotive.ca) projects by Locomotive. +Front-end Boilerplate for projects by Locomotive. + +## Requirements + +| Prerequisite | How to check | How to install | +| --------------- | ------------- | -------------- | +| Node.js 4.1.1 | `node -v` | [nodejs.org](//nodejs.org/) | +| Grunt >= 0.1.13 | `grunt -v` | `npm install -g grunt-cli` | ## Getting started -### Setup project - -1. **Clone the boilerplate module into your own module** - 1. `sh clone.sh {{project-name}}` - - where `{{project-name}}` is the name of the target project. - - Note: on windows, the `sh` command is usually ran from cygwin. -2. **Create and setup the database** - 1. Create an empty database (most people use phpmyadmin...) - 2. Setup the database in `www/config/config.json` -3. **Setup configuration** - 1. Setup the proper `URL` in in `www/config/config.php` - 2. Setup languages and project name, if necessary, in `www/config/config.json` -4. **Initialize Charcoal** - 1. Make sure the Charcoal core is installed - - Using SVN, this is normally done with a `svn:externals` property in `www/` - 2. Setup a local admin user - - Visit `http://{{project-url}}/admin` to start the admin user creation wizard. - -### Install the node modules / grunt - 1. **Get the latest node modules** 1. `npm install -g npm-check-updates` 2. `npm-check-updates -u` @@ -34,29 +21,31 @@ Boilerplate for [`Charcoal`](#http://charcoal.locomotive.ca) projects by Locomot 2. **Run grunt and start coding** - `grunt` - -## Grunt +### Grunt Each Grunt task has it's own file in the `grunt_tasks` folder. -#### BrowserSync +### BrowserSync BrowserSync will automatically inject, refresh and sync all your browsers. Run `grunt sync` -## SCSS +## CSS -### Import order +- We use [Sass](http://sass-lang.com/) for our CSS Preprocessor +- [itcss](http://itcss.io/) CSS architecture +- More Minimal BEM like CSS Syntax: `.block_element -modifier` +- [More Transparent UI Code with Namespaces](http://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/) + +### Sass import order * **Settings:** Global variables, site-wide settings, config switches, etc. * **Tools:** Site-wide mixins and functions. * **Generic:** Low-specificity, far-reaching rulesets (e.g. resets). * **Base:** Unclassed HTML elements (e.g. `a {}`, `blockquote {}`, `address {}`). -* **Objects:** Objects, abstractions, and design patterns (e.g. `.media {}`). -* **Components:** Discrete, complete chunks of UI (e.g. `.carousel {}`). +* **Objects:** Objects, abstractions, and design patterns (e.g. `.o-media {}`). +* **Components:** Discrete, complete chunks of UI (e.g. `.c-carousel {}`). * **Trumps:** High-specificity, very explicit selectors. Overrides and helper - classes (e.g. `.hidden {}`). - -*From [ITCSS](https://twitter.com/itcss_io)* + classes (e.g. `.u-hidden {}`). ### Grid @@ -64,11 +53,11 @@ We are using a simple inline-block grid system. **Usage** -Insert a `grid` block and add `grid__item` elements inside it. +Insert a `o-grid` block and add `o-grid_item` elements inside it. No rows that contain floats, no twelve columns system; just the number of items you want, with the classes names you want, inside a single block. - Include the grid mixins in your components classes. -- Create custom width grid items by including the `grid__item` mixin and adding the widths you need or just include the helpers mixins with fractions like names. +- Create custom width grid items by including the `o-grid_item` mixin and adding the widths you need or just include the helpers mixins with fractions like names. - Add media queries, on the helpers mixins or on your custom components to change the grid items widths, for your content, on different screen sizes. *[Demo](http://codepen.io/AntoineBoulanger/pen/EaLNxe)* @@ -78,3 +67,8 @@ No rows that contain floats, no twelve columns system; just the number of items We included some basic CSS styles and resets to the form elements so we can easily have custom style form elements that work on every browsers. *[Demo](http://codepen.io/AntoineBoulanger/pen/uBJmi)* + +## JavaScript +- We use HTML data attributes to init our JavaScript modules: `data-app`, `data-widget` and `data-template` +- All DOM related JavaScript is hooked to `js-` prefixed HTML classes +- [jQuery](https://jquery.com/) is globally included diff --git a/composer.json b/composer.json deleted file mode 100644 index bf35122..0000000 --- a/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "minimum-stability":"stable", - "require": { - "league/climate": "~3.0", - "locomotivemtl/charcoal-composer-installer":"@dev", - "locomotivemtl/charcoal-legacy": "@dev" - }, - "repositories":[ - { - "type":"vcs", - "url":"https://github.com/mducharme/charcoal-composer-installer" - }, - { - "type":"vcs", - "url":"https://github.com/locomotivemtl/charcoal-legacy" - } - ] -} diff --git a/grunt_tasks/browserSync.js b/grunt_tasks/browserSync.js index d866b87..9f69855 100644 --- a/grunt_tasks/browserSync.js +++ b/grunt_tasks/browserSync.js @@ -1,17 +1,16 @@ module.exports = { dev: { bsFiles: { - src : [ - 'www/modules/boilerplate/assets/styles/dist/*.css' - ,'www/modules/boilerplate/assets/scripts/dist/*.js' - ,'www/modules/boilerplate/assets/templates/*.php' + src: [ + 'www/assets/styles/dist/*.css', + 'www/assets/scripts/dist/*.js' ] }, options: { - proxy: "localhost", + proxy: 'localhost', port: 3000, watchTask: true, notify: false } } -} +}; diff --git a/grunt_tasks/concat.js b/grunt_tasks/concat.js index 956e014..bf07fab 100644 --- a/grunt_tasks/concat.js +++ b/grunt_tasks/concat.js @@ -1,19 +1,17 @@ module.exports = { - app: { src: [ - 'www/modules/boilerplate/assets/scripts/src/app/*.js', - 'www/modules/boilerplate/assets/scripts/src/templates/*.js', - 'www/modules/boilerplate/assets/scripts/src/widgets/*.js', - 'www/modules/boilerplate/assets/scripts/src/app.js' + 'www/assets/scripts/src/app/*.js', + 'www/assets/scripts/src/templates/*.js', + 'www/assets/scripts/src/widgets/*.js', + 'www/assets/scripts/src/app.js' ], - dest: 'www/modules/boilerplate/assets/scripts/dist/app.js' + dest: 'www/assets/scripts/dist/app.js' }, vendors: { src: [ - 'www/modules/boilerplate/assets/scripts/src/vendors/*.js' + 'www/assets/scripts/src/vendors/*.js' ], - dest: 'www/modules/boilerplate/assets/scripts/dist/vendors.js' + dest: 'www/assets/scripts/dist/vendors.js' } - }; diff --git a/grunt_tasks/csscomb.js b/grunt_tasks/csscomb.js index a553e1a..ddd3a10 100644 --- a/grunt_tasks/csscomb.js +++ b/grunt_tasks/csscomb.js @@ -4,8 +4,8 @@ module.exports = { }, build: { expand: true, - cwd: 'www/modules/boilerplate/assets/styles/src/', + cwd: 'www/assets/styles/src/', src: ['**/*.scss', '!base/_fonts.scss'], - dest: 'www/modules/boilerplate/assets/styles/src/' + dest: 'www/assets/styles/src/' } -} +}; diff --git a/grunt_tasks/cssmin.js b/grunt_tasks/cssmin.js index 2c1fd8b..e0ed645 100644 --- a/grunt_tasks/cssmin.js +++ b/grunt_tasks/cssmin.js @@ -2,9 +2,9 @@ module.exports = { combine: { files: [{ expand: true, - cwd: 'www/modules/boilerplate/assets/styles/dist/', + cwd: 'www/assets/styles/dist/', src: '*.css', - dest: 'www/modules/boilerplate/assets/styles/dist/' + dest: 'www/assets/styles/dist/' }] } } diff --git a/grunt_tasks/jshint.js b/grunt_tasks/jshint.js index d6a6eb9..d1a2c7f 100644 --- a/grunt_tasks/jshint.js +++ b/grunt_tasks/jshint.js @@ -1,19 +1,19 @@ module.exports = { options: { - "curly":true, - "eqeqeq": true, - "eqnull": true, - "undef":true, - "unused": true, - browser:true, - "globals":{ - "window":true, - "$":true, - "jQuery":true + 'curly': true, + 'eqeqeq': true, + 'eqnull': true, + 'undef': true, + 'unused': true, + 'browser': true, + 'globals': { + 'window': true, + '$': true, + 'jQuery': true } }, src: [ - 'www/modules/boilerplate/assets/scripts/src/**/*.js', - '!www/modules/boilerplate/assets/scripts/src/vendors/**/*.js' - ], + 'www/assets/scripts/src/**/*.js', + '!www/assets/scripts/src/vendors/**/*.js' + ] } diff --git a/grunt_tasks/jsonlint.js b/grunt_tasks/jsonlint.js deleted file mode 100644 index ecb0491..0000000 --- a/grunt_tasks/jsonlint.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - project:{ - src:[ - '*.json', - '../config/*.json', - '../config/**/*.json', - 'config/*.json', - 'config/**/*.json' - ] - }, - charcoal:{ - src:[ - '../charcoal/*.json', - '../charcoal/core/config/*.json', - '../charcoal/core/config/**/*.json', - '../charcoal/modules/**/config/*.json', - '../charcoal/modules/**/config/**/*.json' - ] - } -} diff --git a/grunt_tasks/phplint.js b/grunt_tasks/phplint.js deleted file mode 100644 index 72f336b..0000000 --- a/grunt_tasks/phplint.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - options: { - swapPath: '/tmp', - phpArgs : { - // add -f for fatal errors - '-lf': null - } - }, - - project: [ - 'code/*.php', - 'code/**/*.php', - 'www/modules/boilerplate/assets/templates/*.php', - 'www/modules/boilerplate/assets/templates/**/*.php' - ], - charcoal: [ - '../charcoal/core/code/*.php', - '../charcoal/core/code/**/*.php', - '../charcoal/modules/**/code/*.php', - '../charcoal/modules/**/code/**/*.php' - ] -} diff --git a/grunt_tasks/postcss.js b/grunt_tasks/postcss.js index 09de80e..cfff7c9 100644 --- a/grunt_tasks/postcss.js +++ b/grunt_tasks/postcss.js @@ -4,15 +4,15 @@ module.exports = { processors: [ require('autoprefixer')({ browsers: ['last 2 versions', '> 1%', 'ie >= 9'] - }), + }) ] }, files: [ { - src : ['www/modules/boilerplate/assets/styles/dist/*.css'], - dest : 'www/modules/boilerplate/assets/styles/dist/', - expand : true, - flatten : true + src: ['www/assets/styles/dist/*.css'], + dest: 'www/assets/styles/dist/', + expand: true, + flatten: true } ] } diff --git a/grunt_tasks/sass.js b/grunt_tasks/sass.js index 658f64e..68e9dee 100644 --- a/grunt_tasks/sass.js +++ b/grunt_tasks/sass.js @@ -4,7 +4,7 @@ module.exports = { }, dist: { files: { - 'www/modules/boilerplate/assets/styles/dist/main.css': 'www/modules/boilerplate/assets/styles/src/main.scss' + 'www/assets/styles/dist/main.css': 'www/assets/styles/src/main.scss' } } } diff --git a/grunt_tasks/svgmin.js b/grunt_tasks/svgmin.js index a798761..682af2e 100644 --- a/grunt_tasks/svgmin.js +++ b/grunt_tasks/svgmin.js @@ -15,9 +15,9 @@ module.exports = { }, dist: { expand: true, - cwd: 'www/modules/boilerplate/assets/images/dist/', + cwd: 'www/assets/images/dist/', src: '*.svg', - dest: 'www/modules/boilerplate/assets/images/dist/', + dest: 'www/assets/images/dist/', ext: '.svg', extDot: 'first' } diff --git a/grunt_tasks/svgstore.js b/grunt_tasks/svgstore.js index 3d0066e..d90f5c0 100644 --- a/grunt_tasks/svgstore.js +++ b/grunt_tasks/svgstore.js @@ -1,10 +1,8 @@ module.exports = { - options: { - - }, - default : { + options: {}, + default: { files: { - 'www/modules/boilerplate/assets/images/dist/svgs.svg': ['www/modules/boilerplate/assets/images/svgs/*.svg'], + 'www/assets/images/dist/svgs.svg': ['www/assets/images/svgs/*.svg'] } } } diff --git a/grunt_tasks/uglify.js b/grunt_tasks/uglify.js index d2b149a..6b48695 100644 --- a/grunt_tasks/uglify.js +++ b/grunt_tasks/uglify.js @@ -2,9 +2,9 @@ module.exports = { target: { files: [{ expand: true, - cwd: 'www/modules/boilerplate/assets/scripts/dist/', + cwd: 'www/assets/scripts/dist/', src: '**/*.js', - dest: 'www/modules/boilerplate/assets/scripts/dist/' + dest: 'www/assets/scripts/dist/' }] } } diff --git a/grunt_tasks/watch.js b/grunt_tasks/watch.js index 5432307..fcee88c 100644 --- a/grunt_tasks/watch.js +++ b/grunt_tasks/watch.js @@ -1,10 +1,10 @@ module.exports = { javascript: { - files: ['www/modules/boilerplate/assets/scripts/src/**/*.js'], + files: ['www/assets/scripts/src/**/*.js'], tasks: ['concat', 'notify:concat'] }, sass: { - files: ['www/modules/boilerplate/assets/styles/src/**/*.scss'], + files: ['www/assets/styles/src/**/*.scss'], tasks: ['sass', 'postcss', 'notify:sass'], options: { spawn: false, @@ -12,7 +12,7 @@ module.exports = { } }, svg: { - files: ['www/modules/boilerplate/assets/images/**/*.svg'], + files: ['www/assets/images/**/*.svg'], tasks: ['svgstore', 'notify:svg'] }, tasks: { diff --git a/setup_boilerplate.php b/setup_boilerplate.php deleted file mode 100644 index 396af34..0000000 --- a/setup_boilerplate.php +++ /dev/null @@ -1,120 +0,0 @@ -description('Boilerplate setup: Rename the boilerplate files and classes'); -$climate->arguments->add([ - 'project_name' => [ - 'longPrefix' => 'name', - 'description' => 'Project (module) name', - 'defaultValue' => '' - ], - 'help' => [ - 'longPrefix' => 'help', - 'description' => 'Prints a usage statement', - 'noValue' => true - ], - 'quiet' => [ - 'prefix' => 'q', - 'longPrefix' => 'quit', - 'description' => 'Disable Output additional information on operations', - 'noValue' => false - ] -]); - -if($climate->arguments->defined('help')) { - $climate->usage(); - die(); -} - -$climate->underline()->out('Charcoal Boilerplate Module Setup'); - -$climate->arguments->parse(); -$project_name = $climate->arguments->get('project_name'); -$verbose = !!$climate->arguments->get('quiet'); -$verbose = true; - -if(!$project_name) { - $input = $climate->input('What is the name of the project?'); - $project_name = strtolower($input->prompt()); -} -if(!validate_project_name($project_name)) { - $climate->red()->out('Invalid project name. Operation aborted.'); - die(); -} - -$climate->bold()->out(sprintf('Using "%s" as project name...', $project_name)); - - - -if(!function_exists('glob_recursive')) { - function glob_recursive($pattern, $flags = 0) - { - $files = glob($pattern, $flags); - foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { - $files = array_merge($files, glob_recursive($dir.'/'.basename($pattern), $flags)); - } - return $files; - } -} - -$climate->out("\n".'Replacing file content...'); -foreach(glob_recursive("www/*") as $filename) { - if(!is_dir($filename)) { - $file = file_get_contents($filename); - $num_replacement1 = 0; - $num_replacement2 = 0; - $content = preg_replace("/boilerplate/", $project_name, $file, -1, $num_replacement1); - $content = preg_replace("/Boilerplate/", ucfirst($project_name), $content, -1, $num_replacement2); - $num_replacements = ($num_replacement1+$num_replacement2); - if($num_replacements > 0) { - file_put_contents($filename, $content); - if($verbose) { - $climate->dim()->out(sprintf('%d occurence(s) of "boilerplate" has been changed to "%s" in file "%s"', $num_replacements, $project_name, $filename)); - } - } - } -} - -$climate->out("\n".'Renaming files and directories'); -$boilerplate_files = glob_recursive("www/*boilerplate*"); -$boilerplate_files = array_reverse($boilerplate_files); -foreach($boilerplate_files as $filename) { - $target_name = preg_replace("/boilerplate/", $project_name, basename($filename)); - $target_name = dirname($filename).'/'.$target_name; - if($target_name != $filename) { - rename($filename, $target_name); - if($verbose) { - $climate->dim()->out(sprintf('%s has been renamed to %s', $filename, $target_name)); - } - } - -} -$boilerplate_files = glob_recursive("www/*Boilerplate*"); -$boilerplate_files = array_reverse($boilerplate_files); -foreach($boilerplate_files as $filename) { - $climate->inline('.'); - $target_name = preg_replace("/Boilerplate/", ucfirst($project_name), basename($filename)); - $target_name = dirname($filename).'/'.$target_name; - if($target_name != $filename) { - rename($filename, $target_name); - if($verbose) { - $climate->dim()->out(sprintf('%s has been renamed to %s', $filename, $target_name)); - } - } -} - -$climate->green()->out("\n".'Success!'); \ No newline at end of file diff --git a/setup_db.php b/setup_db.php deleted file mode 100644 index 27fccc2..0000000 --- a/setup_db.php +++ /dev/null @@ -1,114 +0,0 @@ -out('Charcoal Boilerplate Local Database Setup'); - -$input = $climate->input('What is your database username? ["root"]'); -$input->defaultTo('root'); -$db_user = $input->prompt(); - -$input = $climate->input('What is your database password? [""]'); -$input->defaultTo(''); -$db_password = $input->prompt(); - -$input = $climate->input('What is your database name?'); -$db_name = $input->prompt(); -if($db_name == '') { - $climate->red()->out('Setup aborted. Please enter a database name'); - die(); -} - -$dsn = 'mysql:dbname='.$db_name; - -try { - $dbh = new PDO($dsn, $db_user, $db_password); - $climate->out(sprintf('Database %s already exists.', $db_name)); - $input = $climate->bold()->confirm('Do you want to use this database for this project?'); - if ($input->confirmed()) { - // ... - } - else { - $climate->red()->out('Setup aborted.'); - die(); - } -} -catch (PDOException $e) { - $climate->out(sprintf('Database %s does not exist.', $db_name)); - $input = $climate->bold()->confirm('Do you want to create it?'); - if ($input->confirmed()) { - try { - $dbh = new PDO("mysql:", $db_user, $db_password); - - $res = $dbh->exec("CREATE DATABASE `$db_name`;"); - if(!$res) { - $climate->red()->out('Setup aborted. Could not create database.'); - die(); - } - else { - $climate->green()->out('Database created successfully.'); - } - - } - catch (PDOException $e) { - $climate->red()->out('Setup aborted. Could not create database.'); - die(); - } - } - else { - $climate->red()->out('Setup aborted.'); - die(); - } -} - -// If here, DB is created. Setup config.json -$climate->out('Setting up the database for the project...'); - -$filename = 'www/config/config.json'; -if(!file_exists($filename) || !is_writable($filename)) { - $climate->red()->out('Could not open config.json or file not writeable'); - die(); -} -$config = file_get_contents($filename); -$json = json_decode($config, true); - -$input = $climate->input('What should the database ident be in the config file? ["local"]'); -$input->defaultTo('local'); -$db_ident = $input->prompt(); - -if(!isset($json['databases'])) { - $json['databases'] = []; -} - -$databases = array_keys($json['databases']); -if(in_array($db_ident, $databases)) { - $input = $climate->bold()->confirm('Database ident already exists in the config file. Overwrite?'); - if (!$input->confirmed()) { - $climate->red()->out('Setup aborted.'); - die(); - } -} - -$json['databases'][$db_ident] = [ - 'database' => $db_name, - 'username' => $db_user, - 'password' => $db_password -]; - -$climate->green()->out('Database added successfully to config file.'); - -$input = $climate->bold()->confirm(sprintf('Do you want to use "%s" as the default database ident?', $db_name)); -if ($input->confirmed()) { - $json['default_database'] = $db_ident; -} - -$config = json_encode($json, JSON_PRETTY_PRINT); -$res = file_put_contents($filename, $config); -if($res === false) { - $climate->red()->out('Could not write config file. Setup aborted'); -} -else { - $climate->green()->out('Database configuration successful.'); -} - diff --git a/www/.htaccess b/www/.htaccess index dd94955..446a926 100644 --- a/www/.htaccess +++ b/www/.htaccess @@ -1,115 +1,984 @@ +# Apache Server Configs v2.14.0 | MIT License +# https://github.com/h5bp/server-configs-apache + +# (!) Using `.htaccess` files slows down Apache, therefore, if you have +# access to the main server configuration file (which is usually called +# `httpd.conf`), you should add this logic there. +# +# https://httpd.apache.org/docs/current/howto/htaccess.html. + +# ###################################################################### +# # CROSS-ORIGIN # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Cross-origin requests | +# ---------------------------------------------------------------------- + +# Allow cross-origin requests. +# +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS +# http://enable-cors.org/ +# http://www.w3.org/TR/cors/ + +# +# Header set Access-Control-Allow-Origin "*" +# + +# ---------------------------------------------------------------------- +# | Cross-origin images | +# ---------------------------------------------------------------------- + +# Send the CORS header for images when browsers request it. +# +# https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image +# https://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html + + + + + SetEnvIf Origin ":" IS_CORS + Header set Access-Control-Allow-Origin "*" env=IS_CORS + + + + +# ---------------------------------------------------------------------- +# | Cross-origin web fonts | +# ---------------------------------------------------------------------- + +# Allow cross-origin access to web fonts. + + + + Header set Access-Control-Allow-Origin "*" + + + +# ---------------------------------------------------------------------- +# | Cross-origin resource timing | +# ---------------------------------------------------------------------- + +# Allow cross-origin access to the timing information for all resources. +# +# If a resource isn't served with a `Timing-Allow-Origin` header that +# would allow its timing information to be shared with the document, +# some of the attributes of the `PerformanceResourceTiming` object will +# be set to zero. +# +# http://www.w3.org/TR/resource-timing/ +# http://www.stevesouders.com/blog/2014/08/21/resource-timing-practical-tips/ + +# +# Header set Timing-Allow-Origin: "*" +# + + +# ###################################################################### +# # ERRORS # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Custom error messages/pages | +# ---------------------------------------------------------------------- + +# Customize what Apache returns to the client in case of an error. +# https://httpd.apache.org/docs/current/mod/core.html#errordocument + +ErrorDocument 404 /404.html + +# ---------------------------------------------------------------------- +# | Error prevention | +# ---------------------------------------------------------------------- + +# Disable the pattern matching based on filenames. +# +# This setting prevents Apache from returning a 404 error as the result +# of a rewrite when the directory with the same name does not exist. +# +# https://httpd.apache.org/docs/current/content-negotiation.html#multiviews + +Options -MultiViews + + +# ###################################################################### +# # INTERNET EXPLORER # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Document modes | +# ---------------------------------------------------------------------- + +# Force Internet Explorer 8/9/10 to render pages in the highest mode +# available in the various cases when it may not. +# +# https://hsivonen.fi/doctype/#ie8 +# +# (!) Starting with Internet Explorer 11, document modes are deprecated. +# If your business still relies on older web apps and services that were +# designed for older versions of Internet Explorer, you might want to +# consider enabling `Enterprise Mode` throughout your company. +# +# https://msdn.microsoft.com/en-us/library/ie/bg182625.aspx#docmode +# http://blogs.msdn.com/b/ie/archive/2014/04/02/stay-up-to-date-with-enterprise-mode-for-internet-explorer-11.aspx + + + + Header set X-UA-Compatible "IE=edge" + + # `mod_headers` cannot match based on the content-type, however, + # the `X-UA-Compatible` response header should be send only for + # HTML documents and not for the other resources. + + + Header unset X-UA-Compatible + + + + +# ---------------------------------------------------------------------- +# | Iframes cookies | +# ---------------------------------------------------------------------- + +# Allow cookies to be set from iframes in Internet Explorer. +# +# https://msdn.microsoft.com/en-us/library/ms537343.aspx +# http://www.w3.org/TR/2000/CR-P3P-20001215/ + +# +# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" +# + + +# ###################################################################### +# # MEDIA TYPES AND CHARACTER ENCODINGS # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Media types | +# ---------------------------------------------------------------------- + +# Serve resources with the proper media types (f.k.a. MIME types). +# +# https://www.iana.org/assignments/media-types/media-types.xhtml +# https://httpd.apache.org/docs/current/mod/mod_mime.html#addtype + + + + # Data interchange + + AddType application/atom+xml atom + AddType application/json json map topojson + AddType application/ld+json jsonld + AddType application/rss+xml rss + AddType application/vnd.geo+json geojson + AddType application/xml rdf xml + + + # JavaScript + + # Normalize to standard type. + # https://tools.ietf.org/html/rfc4329#section-7.2 + + AddType application/javascript js + + + # Manifest files + + AddType application/manifest+json webmanifest + AddType application/x-web-app-manifest+json webapp + AddType text/cache-manifest appcache + + + # Media files + + AddType audio/mp4 f4a f4b m4a + AddType audio/ogg oga ogg opus + AddType image/bmp bmp + AddType image/svg+xml svg svgz + AddType image/webp webp + AddType video/mp4 f4v f4p m4v mp4 + AddType video/ogg ogv + AddType video/webm webm + AddType video/x-flv flv + + # Serving `.ico` image files with a different media type + # prevents Internet Explorer from displaying then as images: + # https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee + + AddType image/x-icon cur ico + + + # Web fonts + + AddType application/font-woff woff + AddType application/font-woff2 woff2 + AddType application/vnd.ms-fontobject eot + + # Browsers usually ignore the font media types and simply sniff + # the bytes to figure out the font type. + # https://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern + # + # However, Blink and WebKit based browsers will show a warning + # in the console if the following font types are served with any + # other media types. + + AddType application/x-font-ttf ttc ttf + AddType font/opentype otf + + + # Other + + AddType application/octet-stream safariextz + AddType application/x-bb-appworld bbaw + AddType application/x-chrome-extension crx + AddType application/x-opera-extension oex + AddType application/x-xpinstall xpi + AddType text/vcard vcard vcf + AddType text/vnd.rim.location.xloc xloc + AddType text/vtt vtt + AddType text/x-component htc + + + +# ---------------------------------------------------------------------- +# | Character encodings | +# ---------------------------------------------------------------------- + +# Serve all resources labeled as `text/html` or `text/plain` +# with the media type `charset` parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset + +AddDefaultCharset utf-8 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Serve the following file types with the media type `charset` +# parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset + + + AddCharset utf-8 .atom \ + .bbaw \ + .css \ + .geojson \ + .js \ + .json \ + .jsonld \ + .manifest \ + .rdf \ + .rss \ + .topojson \ + .vtt \ + .webapp \ + .webmanifest \ + .xloc \ + .xml + + + +# ###################################################################### +# # REWRITES # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Rewrite engine | +# ---------------------------------------------------------------------- + +# (1) Turn on the rewrite engine (this is necessary in order for +# the `RewriteRule` directives to work). +# +# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#RewriteEngine +# +# (2) Enable the `FollowSymLinks` option if it isn't already. +# +# https://httpd.apache.org/docs/current/mod/core.html#options +# +# (3) If your web host doesn't allow the `FollowSymlinks` option, +# you need to comment it out or remove it, and then uncomment +# the `Options +SymLinksIfOwnerMatch` line (4), but be aware +# of the performance impact. +# +# https://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks +# +# (4) Some cloud hosting services will require you set `RewriteBase`. +# +# https://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-modrewrite-not-working-on-my-site +# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase +# +# (5) Depending on how your server is set up, you may also need to +# use the `RewriteOptions` directive to enable some options for +# the rewrite engine. +# +# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteoptions +# +# (6) Set %{ENV:PROTO} variable, to allow rewrites to redirect with the +# appropriate schema automatically (http or https). + + + + # (1) + RewriteEngine On + + # (2) + Options +FollowSymlinks + + # (3) + # Options +SymLinksIfOwnerMatch + + # (4) + # RewriteBase / + + # (5) + # RewriteOptions + + # (6) + RewriteCond %{HTTPS} =on + RewriteRule ^ - [env=proto:https] + RewriteCond %{HTTPS} !=on + RewriteRule ^ - [env=proto:http] + + + +# ---------------------------------------------------------------------- +# | Forcing `https://` | +# ---------------------------------------------------------------------- + +# Redirect from the `http://` to the `https://` version of the URL. +# https://wiki.apache.org/httpd/RewriteHTTPToHTTPS + +# +# RewriteEngine On +# RewriteCond %{HTTPS} !=on +# RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] +# + +# ---------------------------------------------------------------------- +# | Suppressing / Forcing the `www.` at the beginning of URLs | +# ---------------------------------------------------------------------- + +# The same content should never be available under two different +# URLs, especially not with and without `www.` at the beginning. +# This can cause SEO problems (duplicate content), and therefore, +# you should choose one of the alternatives and redirect the other +# one. +# +# By default `Option 1` (no `www.`) is activated. +# http://no-www.org/faq.php?q=class_b +# +# If you would prefer to use `Option 2`, just comment out all the +# lines from `Option 1` and uncomment the ones from `Option 2`. +# +# (!) NEVER USE BOTH RULES AT THE SAME TIME! + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 1: rewrite www.example.com → example.com + + + RewriteEngine On + RewriteCond %{HTTPS} !=on + RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 2: rewrite example.com → www.example.com +# +# Be aware that the following might not be a good idea if you use "real" +# subdomains for certain parts of your website. + +# +# RewriteEngine On +# RewriteCond %{HTTPS} !=on +# RewriteCond %{HTTP_HOST} !^www\. [NC] +# RewriteCond %{SERVER_ADDR} !=127.0.0.1 +# RewriteCond %{SERVER_ADDR} !=::1 +# RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] +# + + +# ###################################################################### +# # SECURITY # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Clickjacking | +# ---------------------------------------------------------------------- + +# Protect website against clickjacking. +# +# The example below sends the `X-Frame-Options` response header with +# the value `DENY`, informing browsers not to display the content of +# the web page in any frame. +# +# This might not be the best setting for everyone. You should read +# about the other two possible values the `X-Frame-Options` header +# field can have: `SAMEORIGIN` and `ALLOW-FROM`. +# https://tools.ietf.org/html/rfc7034#section-2.1. +# +# Keep in mind that while you could send the `X-Frame-Options` header +# for all of your website’s pages, this has the potential downside that +# it forbids even non-malicious framing of your content (e.g.: when +# users visit your website using a Google Image Search results page). +# +# Nonetheless, you should ensure that you send the `X-Frame-Options` +# header for all pages that allow a user to make a state changing +# operation (e.g: pages that contain one-click purchase links, checkout +# or bank-transfer confirmation pages, pages that make permanent +# configuration changes, etc.). +# +# Sending the `X-Frame-Options` header can also protect your website +# against more than just clickjacking attacks: +# https://cure53.de/xfo-clickjacking.pdf. +# +# https://tools.ietf.org/html/rfc7034 +# http://blogs.msdn.com/b/ieinternals/archive/2010/03/30/combating-clickjacking-with-x-frame-options.aspx +# https://www.owasp.org/index.php/Clickjacking + +# + +# Header set X-Frame-Options "DENY" + +# # `mod_headers` cannot match based on the content-type, however, +# # the `X-Frame-Options` response header should be send only for +# # HTML documents and not for the other resources. + +# +# Header unset X-Frame-Options +# + +# + +# ---------------------------------------------------------------------- +# | Content Security Policy (CSP) | +# ---------------------------------------------------------------------- + +# Mitigate the risk of cross-site scripting and other content-injection +# attacks. +# +# This can be done by setting a `Content Security Policy` which +# whitelists trusted sources of content for your website. +# +# The example header below allows ONLY scripts that are loaded from +# the current website's origin (no inline scripts, no CDN, etc). +# That almost certainly won't work as-is for your website! +# +# To make things easier, you can use an online CSP header generator +# such as: http://cspisawesome.com/. +# +# http://content-security-policy.com/ +# http://www.html5rocks.com/en/tutorials/security/content-security-policy/ +# http://www.w3.org/TR/CSP11/). + +# + +# Header set Content-Security-Policy "script-src 'self'; object-src 'self'" + +# # `mod_headers` cannot match based on the content-type, however, +# # the `Content-Security-Policy` response header should be send +# # only for HTML documents and not for the other resources. + +# +# Header unset Content-Security-Policy +# + +# + +# ---------------------------------------------------------------------- +# | File access | +# ---------------------------------------------------------------------- + +# Block access to directories without a default document. +# +# You should leave the following uncommented, as you shouldn't allow +# anyone to surf through every directory on your server (which may +# includes rather private places such as the CMS's directories). + + + Options -Indexes + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to all hidden files and directories with the exception of +# the visible content from within the `/.well-known/` hidden directory. +# +# These types of files usually contain user preferences or the preserved +# state of an utility, and can include rather private places like, for +# example, the `.git` or `.svn` directories. +# +# The `/.well-known/` directory represents the standard (RFC 5785) path +# prefix for "well-known locations" (e.g.: `/.well-known/manifest.json`, +# `/.well-known/keybase.txt`), and therefore, access to its visible +# content should not be blocked. +# +# https://www.mnot.net/blog/2010/04/07/well-known +# https://tools.ietf.org/html/rfc5785 + + + RewriteEngine On + RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC] + RewriteCond %{SCRIPT_FILENAME} -d [OR] + RewriteCond %{SCRIPT_FILENAME} -f + RewriteRule "(^|/)\." - [F] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to files that can expose sensitive information. +# +# By default, block access to backup and source files that may be +# left by some text editors and can pose a security risk when anyone +# has access to them. +# +# http://feross.org/cmsploit/ +# +# (!) Update the `` regular expression from below to +# include any files that might end up on your production server and +# can expose sensitive information about your website. These files may +# include: configuration files, files that contain metadata about the +# project (e.g.: project dependencies), build scripts, etc.. + + + + # Apache < 2.3 + + Order allow,deny + Deny from all + Satisfy All + + + # Apache ≥ 2.3 + + Require all denied + - - - Header set Access-Control-Allow-Origin "*" - +# ---------------------------------------------------------------------- +# | HTTP Strict Transport Security (HSTS) | +# ---------------------------------------------------------------------- +# Force client-side SSL redirection. +# +# If a user types `example.com` in their browser, even if the server +# redirects them to the secure version of the website, that still leaves +# a window of opportunity (the initial HTTP connection) for an attacker +# to downgrade or redirect the request. +# +# The following header ensures that browser will ONLY connect to your +# server via HTTPS, regardless of what the users type in the browser's +# address bar. +# +# (!) Remove the `includeSubDomains` optional directive if the website's +# subdomains are not using HTTPS. +# +# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ +# https://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14#section-6.1 +# http://blogs.msdn.com/b/ieinternals/archive/2014/08/18/hsts-strict-transport-security-attacks-mitigations-deployment-https.aspx + +# +# Header always set Strict-Transport-Security "max-age=16070400; includeSubDomains" +# + +# ---------------------------------------------------------------------- +# | Reducing MIME type security risks | +# ---------------------------------------------------------------------- + +# Prevent some browsers from MIME-sniffing the response. +# +# This reduces exposure to drive-by download attacks and cross-origin +# data leaks, and should be left uncommented, especially if the server +# is serving user-uploaded content or content that could potentially be +# treated as executable by the browser. +# +# http://www.slideshare.net/hasegawayosuke/owasp-hasegawa +# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx +# https://msdn.microsoft.com/en-us/library/ie/gg622941.aspx +# https://mimesniff.spec.whatwg.org/ + + + Header set X-Content-Type-Options "nosniff" + + +# ---------------------------------------------------------------------- +# | Reflected Cross-Site Scripting (XSS) attacks | +# ---------------------------------------------------------------------- + +# (1) Try to re-enable the cross-site scripting (XSS) filter built +# into most web browsers. +# +# The filter is usually enabled by default, but in some cases it +# may be disabled by the user. However, in Internet Explorer for +# example, it can be re-enabled just by sending the +# `X-XSS-Protection` header with the value of `1`. +# +# (2) Prevent web browsers from rendering the web page if a potential +# reflected (a.k.a non-persistent) XSS attack is detected by the +# filter. +# +# By default, if the filter is enabled and browsers detect a +# reflected XSS attack, they will attempt to block the attack +# by making the smallest possible modifications to the returned +# web page. +# +# Unfortunately, in some browsers (e.g.: Internet Explorer), +# this default behavior may allow the XSS filter to be exploited, +# thereby, it's better to inform browsers to prevent the rendering +# of the page altogether, instead of attempting to modify it. +# +# https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities +# +# (!) Do not rely on the XSS filter to prevent XSS attacks! Ensure that +# you are taking all possible measures to prevent XSS attacks, the +# most obvious being: validating and sanitizing your website's inputs. +# +# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx +# http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx +# https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29 + +# + +# # (1) (2) +# Header set X-XSS-Protection "1; mode=block" + +# # `mod_headers` cannot match based on the content-type, however, +# # the `X-XSS-Protection` response header should be send only for +# # HTML documents and not for the other resources. + +# +# Header unset X-XSS-Protection +# + +# + +# ---------------------------------------------------------------------- +# | Server-side technology information | +# ---------------------------------------------------------------------- + +# Remove the `X-Powered-By` response header that: +# +# * is set by some frameworks and server-side languages +# (e.g.: ASP.NET, PHP), and its value contains information +# about them (e.g.: their name, version number) +# +# * doesn't provide any value as far as users are concern, +# and in some cases, the information provided by it can +# be used by attackers +# +# (!) If you can, you should disable the `X-Powered-By` header from the +# language / framework level (e.g.: for PHP, you can do that by setting +# `expose_php = off` in `php.ini`) +# +# https://php.net/manual/en/ini.core.php#ini.expose-php + + + Header unset X-Powered-By + + +# ---------------------------------------------------------------------- +# | Server software information | +# ---------------------------------------------------------------------- + +# Prevent Apache from adding a trailing footer line containing +# information about the server to the server-generated documents +# (e.g.: error messages, directory listings, etc.) +# +# https://httpd.apache.org/docs/current/mod/core.html#serversignature + +ServerSignature Off + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Prevent Apache from sending in the `Server` response header its +# exact version number, the description of the generic OS-type or +# information about its compiled-in modules. +# +# (!) The `ServerTokens` directive will only work in the main server +# configuration file, so don't try to enable it in the `.htaccess` file! +# +# https://httpd.apache.org/docs/current/mod/core.html#servertokens + +#ServerTokens Prod + + +# ###################################################################### +# # WEB PERFORMANCE # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Compression | +# ---------------------------------------------------------------------- - - - SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s,?\s(gzip|deflate)?|X{4,13}|~{4,13}|-{4,13})$ HAVE_Accept-Encoding - RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding - - + # Force compression for mangled `Accept-Encoding` request headers + # https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html - - # Legacy versions of Apache - AddOutputFilterByType DEFLATE text/html text/plain text/css application/json - AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript - AddOutputFilterByType DEFLATE text/xml application/xml text/x-component - + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + - - SetOutputFilter DEFLATE - - + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Compress all output labeled with one of the following media types. + # + # (!) For Apache versions below version 2.3.7 you don't need to + # enable `mod_filter` and can remove the `` + # and `` lines as `AddOutputFilterByType` is still in + # the core directives. + # + # https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype + + AddOutputFilterByType DEFLATE "application/atom+xml" \ + "application/javascript" \ + "application/json" \ + "application/ld+json" \ + "application/manifest+json" \ + "application/rdf+xml" \ + "application/rss+xml" \ + "application/schema+json" \ + "application/vnd.geo+json" \ + "application/vnd.ms-fontobject" \ + "application/x-font-ttf" \ + "application/x-javascript" \ + "application/x-web-app-manifest+json" \ + "application/xhtml+xml" \ + "application/xml" \ + "font/eot" \ + "font/opentype" \ + "image/bmp" \ + "image/svg+xml" \ + "image/vnd.microsoft.icon" \ + "image/x-icon" \ + "text/cache-manifest" \ + "text/css" \ + "text/html" \ + "text/javascript" \ + "text/plain" \ + "text/vcard" \ + "text/vnd.rim.location.xloc" \ + "text/vtt" \ + "text/x-component" \ + "text/x-cross-domain-policy" \ + "text/xml" - - ExpiresActive on + - ExpiresDefault "access plus 1 month" - ExpiresByType text/cache-manifest "access plus 0 seconds" - ExpiresByType text/html "access plus 0 seconds" + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ExpiresByType text/xml "access plus 0 seconds" - ExpiresByType application/xml "access plus 0 seconds" - ExpiresByType application/json "access plus 1 day" + # Map the following filename extensions to the specified + # encoding type in order to make Apache serve the file types + # with the appropriate `Content-Encoding` response header + # (do note that this will NOT make Apache compress them!). + # + # If these files types would be served without an appropriate + # `Content-Enable` response header, client applications (e.g.: + # browsers) wouldn't know that they first need to uncompress + # the response, and thus, wouldn't be able to understand the + # content. + # + # https://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding - ExpiresByType application/rss+xml "access plus 1 hour" - - ExpiresByType image/x-icon "access plus 1 week" - - ExpiresByType image/gif "access plus 1 month" - ExpiresByType image/png "access plus 1 month" - ExpiresByType image/jpg "access plus 1 month" - ExpiresByType image/jpeg "access plus 1 month" - ExpiresByType video/ogg "access plus 1 month" - ExpiresByType audio/ogg "access plus 1 month" - ExpiresByType video/mp4 "access plus 1 month" - ExpiresByType video/webm "access plus 1 month" - ExpiresByType audio/webm "access plus 1 month" - - ExpiresByType text/x-component "access plus 1 month" - - ExpiresByType font/truetype "access plus 1 month" - ExpiresByType font/opentype "access plus 1 month" - ExpiresByType application/x-font-woff "access plus 1 month" - ExpiresByType image/svg+xml "access plus 1 month" - ExpiresByType application/vnd.ms-fontobject "access plus 1 month" - - ExpiresByType text/css "access plus 1 year" - ExpiresByType application/javascript "access plus 1 year" - ExpiresByType text/javascript "access plus 1 year" - - - Header append Cache-Control "public" - + + AddEncoding gzip svgz + +# ---------------------------------------------------------------------- +# | Content transformation | +# ---------------------------------------------------------------------- +# Prevent intermediate caches or proxies (e.g.: such as the ones +# used by mobile network providers) from modifying the website's +# content. +# +# https://tools.ietf.org/html/rfc2616#section-14.9.5 +# +# (!) If you are using `mod_pagespeed`, please note that setting +# the `Cache-Control: no-transform` response header will prevent +# `PageSpeed` from rewriting `HTML` files, and, if the +# `ModPagespeedDisableRewriteOnNoTransform` directive isn't set +# to `off`, also from rewriting other resources. +# +# https://developers.google.com/speed/pagespeed/module/configuration#notransform + +# +# Header merge Cache-Control "no-transform" +# + +# ---------------------------------------------------------------------- +# | ETags | +# ---------------------------------------------------------------------- + +# Remove `ETags` as resources are sent with far-future expires headers. +# +# https://developer.yahoo.com/performance/rules.html#etags +# https://tools.ietf.org/html/rfc7232#section-2.3 + +# `FileETag None` doesn't work in all cases. - Header unset ETag + Header unset ETag FileETag None - -RewriteRule get-asset-(\w*) admin/assets?t=$1&%{QUERY_STRING} [L,NC] - Options +FollowSymlinks - RewriteEngine On - +# ---------------------------------------------------------------------- +# | Expires headers | +# ---------------------------------------------------------------------- + +# Serve resources with far-future expires headers. +# +# (!) If you don't control versioning with filename-based +# cache busting, you should consider lowering the cache times +# to something like one week. +# +# https://httpd.apache.org/docs/current/mod/mod_expires.html + + + + ExpiresActive on + ExpiresDefault "access plus 1 month" + + # CSS + + ExpiresByType text/css "access plus 1 year" -Options -MultiViews + # Data interchange -ErrorDocument 400 /400.php -ErrorDocument 401 /401.php -ErrorDocument 402 /402.php -ErrorDocument 403 /403.php -ErrorDocument 404 /404.php + ExpiresByType application/atom+xml "access plus 1 hour" + ExpiresByType application/rdf+xml "access plus 1 hour" + ExpiresByType application/rss+xml "access plus 1 hour" -AddDefaultCharset utf-8 -AddCharset utf-8 .html .css .js .xml .json .rss + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/ld+json "access plus 0 seconds" + ExpiresByType application/schema+json "access plus 0 seconds" + ExpiresByType application/vnd.geo+json "access plus 0 seconds" + ExpiresByType application/xml "access plus 0 seconds" + ExpiresByType text/xml "access plus 0 seconds" -Options -Indexes - - RewriteRule "(^|/)\." - [F] + # Favicon (cannot be renamed!) and cursor images - # Sections - RewriteRule ^([a-zA-Z]{2})/([0-9]+)/([^/]+) index.php?%{query_string}&s=$2&lang=$1&slug=$3 + ExpiresByType image/vnd.microsoft.icon "access plus 1 week" + ExpiresByType image/x-icon "access plus 1 week" - # Charcoal - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ charcoal.php?action=$1&%{QUERY_STRING} [PT,L] + # HTML + + ExpiresByType text/html "access plus 0 seconds" + + + # JavaScript + + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType application/x-javascript "access plus 1 year" + ExpiresByType text/javascript "access plus 1 year" + + + # Manifest files + + ExpiresByType application/manifest+json "access plus 1 week" + ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" + ExpiresByType text/cache-manifest "access plus 0 seconds" + + + # Media files + + ExpiresByType audio/ogg "access plus 1 month" + ExpiresByType image/bmp "access plus 1 month" + ExpiresByType image/gif "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType image/svg+xml "access plus 1 month" + ExpiresByType image/webp "access plus 1 month" + ExpiresByType video/mp4 "access plus 1 month" + ExpiresByType video/ogg "access plus 1 month" + ExpiresByType video/webm "access plus 1 month" + + + # Web fonts + + # Embedded OpenType (EOT) + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" + ExpiresByType font/eot "access plus 1 month" + + # OpenType + ExpiresByType font/opentype "access plus 1 month" + + # TrueType + ExpiresByType application/x-font-ttf "access plus 1 month" + + # Web Open Font Format (WOFF) 1.0 + ExpiresByType application/font-woff "access plus 1 month" + ExpiresByType application/x-font-woff "access plus 1 month" + ExpiresByType font/woff "access plus 1 month" + + # Web Open Font Format (WOFF) 2.0 + ExpiresByType application/font-woff2 "access plus 1 month" + + + # Other + + ExpiresByType text/x-cross-domain-policy "access plus 1 week" +# ---------------------------------------------------------------------- +# | File concatenation | +# ---------------------------------------------------------------------- + +# Allow concatenation from within specific files. +# +# e.g.: +# +# If you have the following lines in a file called, for +# example, `main.combined.js`: +# +# +# +# +# Apache will replace those lines with the content of the +# specified files. + +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES application/javascript \ +# application/x-javascript \ +# text/javascript +# SetOutputFilter INCLUDES +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES text/css +# SetOutputFilter INCLUDES +# +# + +# ---------------------------------------------------------------------- +# | Filename-based cache busting | +# ---------------------------------------------------------------------- + +# If you're not using a build process to manage your filename version +# revving, you might want to consider enabling the following directives +# to route all requests such as `/style.12345.css` to `/style.css`. +# +# To understand why this is important and even a better solution than +# using something like `*.css?v231`, please see: +# http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/ + +# +# RewriteEngine On +# RewriteCond %{REQUEST_FILENAME} !-f +# RewriteRule ^(.+)\.(\d+)\.(bmp|css|cur|gif|ico|jpe?g|js|png|svgz?|webp|webmanifest)$ $1.$3 [L] +# diff --git a/www/modules/boilerplate/assets/emails/static/index.html b/www/assets/emails/static/index.html similarity index 100% rename from www/modules/boilerplate/assets/emails/static/index.html rename to www/assets/emails/static/index.html diff --git a/www/modules/boilerplate/assets/fonts/.gitkeep b/www/assets/fonts/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/fonts/.gitkeep rename to www/assets/fonts/.gitkeep diff --git a/www/modules/boilerplate/assets/images/apple-touch-icon.png b/www/assets/images/apple-touch-icon.png similarity index 100% rename from www/modules/boilerplate/assets/images/apple-touch-icon.png rename to www/assets/images/apple-touch-icon.png diff --git a/www/modules/boilerplate/assets/images/dist/.gitkeep b/www/assets/images/dist/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/images/dist/.gitkeep rename to www/assets/images/dist/.gitkeep diff --git a/www/modules/boilerplate/assets/images/favicon.png b/www/assets/images/favicon.png similarity index 100% rename from www/modules/boilerplate/assets/images/favicon.png rename to www/assets/images/favicon.png diff --git a/www/modules/boilerplate/assets/images/svgs/.gitkeep b/www/assets/images/svgs/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/images/svgs/.gitkeep rename to www/assets/images/svgs/.gitkeep diff --git a/www/modules/boilerplate/assets/scripts/dist/app.js b/www/assets/scripts/dist/app.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/dist/app.js rename to www/assets/scripts/dist/app.js diff --git a/www/modules/boilerplate/assets/scripts/dist/jquery-1.11.3.min.js b/www/assets/scripts/dist/jquery-1.11.3.min.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/dist/jquery-1.11.3.min.js rename to www/assets/scripts/dist/jquery-1.11.3.min.js diff --git a/www/modules/boilerplate/assets/scripts/dist/vendors.js b/www/assets/scripts/dist/vendors.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/dist/vendors.js rename to www/assets/scripts/dist/vendors.js diff --git a/www/modules/boilerplate/assets/scripts/src/app.js b/www/assets/scripts/src/app.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/src/app.js rename to www/assets/scripts/src/app.js diff --git a/www/modules/boilerplate/assets/scripts/src/app/globals.js b/www/assets/scripts/src/app/globals.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/src/app/globals.js rename to www/assets/scripts/src/app/globals.js diff --git a/www/modules/boilerplate/assets/scripts/src/templates/generic.js b/www/assets/scripts/src/templates/generic.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/src/templates/generic.js rename to www/assets/scripts/src/templates/generic.js diff --git a/www/modules/boilerplate/assets/scripts/src/vendors/.gitkeep b/www/assets/scripts/src/vendors/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/scripts/src/vendors/.gitkeep rename to www/assets/scripts/src/vendors/.gitkeep diff --git a/www/modules/boilerplate/assets/scripts/src/widgets/generic.js b/www/assets/scripts/src/widgets/generic.js similarity index 100% rename from www/modules/boilerplate/assets/scripts/src/widgets/generic.js rename to www/assets/scripts/src/widgets/generic.js diff --git a/www/modules/boilerplate/assets/styles/dist/main.css b/www/assets/styles/dist/main.css similarity index 100% rename from www/modules/boilerplate/assets/styles/dist/main.css rename to www/assets/styles/dist/main.css diff --git a/www/modules/boilerplate/assets/styles/src/_imports.scss b/www/assets/styles/src/_imports.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/_imports.scss rename to www/assets/styles/src/_imports.scss diff --git a/www/modules/boilerplate/assets/styles/src/base/_base.scss b/www/assets/styles/src/base/_base.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/base/_base.scss rename to www/assets/styles/src/base/_base.scss diff --git a/www/modules/boilerplate/assets/styles/src/base/_fonts.scss b/www/assets/styles/src/base/_fonts.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/base/_fonts.scss rename to www/assets/styles/src/base/_fonts.scss diff --git a/www/modules/boilerplate/assets/styles/src/base/_headings.scss b/www/assets/styles/src/base/_headings.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/base/_headings.scss rename to www/assets/styles/src/base/_headings.scss diff --git a/www/modules/boilerplate/assets/styles/src/components/.gitkeep b/www/assets/styles/src/components/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/styles/src/components/.gitkeep rename to www/assets/styles/src/components/.gitkeep diff --git a/www/modules/boilerplate/assets/styles/src/generic/_button.scss b/www/assets/styles/src/generic/_button.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/generic/_button.scss rename to www/assets/styles/src/generic/_button.scss diff --git a/www/modules/boilerplate/assets/styles/src/generic/_form.scss b/www/assets/styles/src/generic/_form.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/generic/_form.scss rename to www/assets/styles/src/generic/_form.scss diff --git a/www/modules/boilerplate/assets/styles/src/generic/_generic.scss b/www/assets/styles/src/generic/_generic.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/generic/_generic.scss rename to www/assets/styles/src/generic/_generic.scss diff --git a/www/modules/boilerplate/assets/styles/src/generic/_normalize.scss b/www/assets/styles/src/generic/_normalize.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/generic/_normalize.scss rename to www/assets/styles/src/generic/_normalize.scss diff --git a/www/modules/boilerplate/assets/styles/src/main.scss b/www/assets/styles/src/main.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/main.scss rename to www/assets/styles/src/main.scss diff --git a/www/modules/boilerplate/assets/styles/src/objects/_button.scss b/www/assets/styles/src/objects/_button.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/objects/_button.scss rename to www/assets/styles/src/objects/_button.scss diff --git a/www/modules/boilerplate/assets/styles/src/objects/_container.scss b/www/assets/styles/src/objects/_container.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/objects/_container.scss rename to www/assets/styles/src/objects/_container.scss diff --git a/www/modules/boilerplate/assets/styles/src/objects/_form.scss b/www/assets/styles/src/objects/_form.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/objects/_form.scss rename to www/assets/styles/src/objects/_form.scss diff --git a/www/modules/boilerplate/assets/styles/src/objects/_grid.scss b/www/assets/styles/src/objects/_grid.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/objects/_grid.scss rename to www/assets/styles/src/objects/_grid.scss diff --git a/www/modules/boilerplate/assets/styles/src/settings/_settings.scss b/www/assets/styles/src/settings/_settings.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/settings/_settings.scss rename to www/assets/styles/src/settings/_settings.scss diff --git a/www/modules/boilerplate/assets/styles/src/templates/.gitkeep b/www/assets/styles/src/templates/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/styles/src/templates/.gitkeep rename to www/assets/styles/src/templates/.gitkeep diff --git a/www/modules/boilerplate/assets/styles/src/tools/_tools.scss b/www/assets/styles/src/tools/_tools.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/tools/_tools.scss rename to www/assets/styles/src/tools/_tools.scss diff --git a/www/modules/boilerplate/assets/styles/src/trumps/_helpers.scss b/www/assets/styles/src/trumps/_helpers.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/trumps/_helpers.scss rename to www/assets/styles/src/trumps/_helpers.scss diff --git a/www/modules/boilerplate/assets/styles/src/trumps/_trumps.scss b/www/assets/styles/src/trumps/_trumps.scss similarity index 100% rename from www/modules/boilerplate/assets/styles/src/trumps/_trumps.scss rename to www/assets/styles/src/trumps/_trumps.scss diff --git a/www/modules/boilerplate/assets/styles/src/vendors/.gitkeep b/www/assets/styles/src/vendors/.gitkeep similarity index 100% rename from www/modules/boilerplate/assets/styles/src/vendors/.gitkeep rename to www/assets/styles/src/vendors/.gitkeep diff --git a/www/charcoal.php b/www/charcoal.php deleted file mode 100644 index ee2c516..0000000 --- a/www/charcoal.php +++ /dev/null @@ -1,16 +0,0 @@ - + @@ -9,16 +10,16 @@ Boilerplate - - + + - + - - - + + + diff --git a/www/index.php b/www/index.php deleted file mode 100644 index 6564619..0000000 --- a/www/index.php +++ /dev/null @@ -1,18 +0,0 @@ - Boilerplate_Config::get_latest()->default_section -]; -Boilerplate_Module::init($opts); - diff --git a/www/modules/boilerplate/assets/templates/boilerplate.action.contact.php b/www/modules/boilerplate/assets/templates/boilerplate.action.contact.php deleted file mode 100644 index ca8e83f..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.action.contact.php +++ /dev/null @@ -1,123 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2012-08-01 -* @link http://locomotive.ca -* @since Version 2014-07-01 -*/ - - -// Get referer -$referer = ''; -if(isset($_POST['referer'])) { - $referer = filter_input(INPUT_POST, 'referer', FILTER_SANITIZE_STRING); -} -else if(isset($_SERVER['HTTP_REFERER'])) { - $referer = $_SERVER['HTTP_REFERER']; -} -else { - // None set: Use contact form - $referer = Charcoal::obj('CMS_Section')->load_from('ident', 'contact')->url(); -} - -function _header_error($msg='') -{ - global $referer; - - if(headers_sent()) { - // Error, cant send headers. Should never happened - die(); - } - header('Location: '.$referer.'?contact-success=0&msg='.urlencode($msg)); - die(); - -} - -function _header_success($msg='') -{ - global $referer; - - if(headers_sent()) { - // Error, cant send headers. Should never happened - die(); - } - header('Location: '.$referer.'?contact-success=1&msg='.urlencode($msg)); - die(); - die(); -} - -// Validate CSRF token -$csrf_token = filter_input(INPUT_POST, 'csrf-token', FILTER_SANITIZE_STRING); -if(!Charcoal::token_validate($csrf_token, 'boilerplate.contact')) { - Charcoal::feedback('error', 'send_contact', _t("Your session has expired. Please try again")); - _headers_error(); -} - -// Create and save the Contact object -$contact = Charcoal::obj('CMS_Contact'); -$contact->from_flat_data($_POST); -// Hardcoded to the general contact category -$contact->category = 1; - -$validations = $contact->validate(); -if(!$validations){ - //pre($contact); - // Saving the contact sends the email confirmatino automatically, according to the category - $contact_id = $contact->save(); - if($contact_id) { - _header_success()_t("Successful."); - } - else { - _headers_error(_t("Error saving contact")); - } -} -else { - $msg = ''; - foreach($validations as $property=>$messages){ - foreach($messages as $m){ - $msg .= $m.'
'; - } - } - - _headers_error($msg); -} diff --git a/www/modules/boilerplate/assets/templates/boilerplate.action.newsletter.php b/www/modules/boilerplate/assets/templates/boilerplate.action.newsletter.php deleted file mode 100644 index 0b1a566..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.action.newsletter.php +++ /dev/null @@ -1,13 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ \ No newline at end of file diff --git a/www/modules/boilerplate/assets/templates/boilerplate.contact.php b/www/modules/boilerplate/assets/templates/boilerplate.contact.php deleted file mode 100644 index 356ddac..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.contact.php +++ /dev/null @@ -1,38 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - -?> -{{>boilerplate.inc.header}} - -
- -

{{section.p.title}}

-

{{section.p.subtitle}}

- -
- {{section.p.summary.display}} -
- -
- {{section.p.content.display}} -
- -
- -
- -
- -{{>boilerplate.inc.footer}} diff --git a/www/modules/boilerplate/assets/templates/boilerplate.content.php b/www/modules/boilerplate/assets/templates/boilerplate.content.php deleted file mode 100644 index efd51f7..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.content.php +++ /dev/null @@ -1,35 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - -?> -{{>boilerplate.inc.header}} - -
- -

{{section.p.title}}

-

{{section.p.subtitle}}

- -
- {{section.p.summary.display}} -
- -
- {{section.p.content.display}} -
- - -
- -{{>boilerplate.inc.footer}} - diff --git a/www/modules/boilerplate/assets/templates/boilerplate.home.php b/www/modules/boilerplate/assets/templates/boilerplate.home.php deleted file mode 100644 index a0deca8..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.home.php +++ /dev/null @@ -1,25 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -?> -{{>boilerplate.inc.header}} - -
- -

{{section.p.title}}

- -
- -{{>boilerplate.inc.footer}} diff --git a/www/modules/boilerplate/assets/templates/boilerplate.inc.footer.php b/www/modules/boilerplate/assets/templates/boilerplate.inc.footer.php deleted file mode 100644 index 001e46e..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.inc.footer.php +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/www/modules/boilerplate/assets/templates/boilerplate.inc.header.php b/www/modules/boilerplate/assets/templates/boilerplate.inc.header.php deleted file mode 100644 index 54f9e73..0000000 --- a/www/modules/boilerplate/assets/templates/boilerplate.inc.header.php +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - {{meta_title}} - - - - - - - - {{opengraph_tags}} - - {{! Only include analytics if it is set in the configuration}} - {{#cfg.google_analytics}} - - {{/cfg.google_analytics}} - - {{!A final chance to have custom headers tags (like styles or scripts) in the controllers}} - {{extra_header_content}} - - - diff --git a/www/modules/boilerplate/code/boilerplate.config.php b/www/modules/boilerplate/code/boilerplate.config.php deleted file mode 100644 index e985642..0000000 --- a/www/modules/boilerplate/code/boilerplate.config.php +++ /dev/null @@ -1,181 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - - -/** -* Boilerplate configuration -* -* The latest (current) object can always be used by calling: -* ```php -* $cfg = \Boilerplate\Config::get_latest(); -* ``` -* -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Objects -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Boilerplate_Config extends \Charcoal_Object -{ - /** - * Google Analytics code - * @var string $google_analytics - * @see Property_String - */ - public $google_analytics; - - /** - * Default section - * The section to load when reaching the default base URL. - * Also, usually the section to load when clicking on the main link / logo - * @var mixed $default_section - * @see Property_Object - * @see CMS_Section - */ - public $default_section; - /** - * Default lang - * @var string $default_lang - * @see Property_Lang - */ - public $default_lang; - - /** - * URL of the related facebook page - * @var string $social_facebook_url - * @see Property_Url - */ - public $social_facebook_url; - /** - * URL of the related twitter account - * @var string $social_twitter_url - * @see Property_Url - */ - public $social_twitter_url; - /** - * URL of the related google+ page - * @var string $social_facebook_url - * @see Property_Url - */ - public $social_google_url; - /** - * URL of the related instagram page - * @var string $social_instagram_url - * @see Property_Url - */ - public $social_instagram_url; - /** - * URL of the related flickr page - * @var string $social_flickr_url - * @see Property_Url - */ - public $social_flickr_url; - /** - * URL of the related youtube page - * @var string $social_youtube_url - * @see Property_Url - */ - public $social_youtube_url; - - /** - * Default title, if none is specified - * - l10n: true - * @var mixed $meta_default_title - * @see Property_String - */ - public $meta_default_title; - /** - * Default title, if none is specified - * - l10n: true - * @var mixed $meta_default_prefix - * @see Property_String - */ - public $meta_title_prefix; - /** - * Default title, if none is specified - * - l10n: true - * @var mixed $meta_default_suffix - * @see Property_String - */ - public $meta_title_suffix; - /** - * Default title, if none is specified - * - l10n: true - * @var mixed $meta_default_description - * @see Property_Text - */ - public $meta_default_description; - /** - * Default title, if none is specified - * - l10n: true - * - upload_path: "uploads/config/meta_image/" - * @var mixed $meta_default_image - * @see Property_Image - */ - public $meta_default_image; - /** - * Default meta-keywords, if none is specified - * - l10n: true - * - multiple: true - * @var mixed $meta_default_keywords - * @see Property_String - */ - public $meta_default_keywords; - - /** - * Static getter of the latest config - * - * This is the preferred way of loading the Configuration object. - * - * It will only be loaded 1 time from the configuration, otherwie, it will be stored - * in a static variable. - * - * @return static Corim_Config - * @todo Use APC instead of static, if available - */ - static public function get_latest() - { - static $_cached; - - $cache_key = 'latest_boilerplate_config'; - - if($_cached instanceof self) { - // Load from static if possible - return $_cached; - } - else { - // Load from DB otherwise - $_cached = Charcoal::obj( __CLASS__ ); - $_cached->load_latest(); - return $_cached; - } - } - - /** - * @return Corim_Config - */ - public function load_latest() - { - // Hardcoded to ID 1 - $this->load_key('id', 1); - - // Chainable - return $this; - } -} \ No newline at end of file diff --git a/www/modules/boilerplate/code/boilerplate.module.php b/www/modules/boilerplate/code/boilerplate.module.php deleted file mode 100644 index c341ef3..0000000 --- a/www/modules/boilerplate/code/boilerplate.module.php +++ /dev/null @@ -1,91 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -/** -* The Boilerplate Module class -* -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Objects -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Boilerplate_Module extends Charcoal_Module -{ - /** - * Module initialisation - * - * This function should act as both the initialization of the module and the front-page main controller. - * - * ## Options - * - default_action - * - default_section - * - default_lang - * - * @param array $opts - * - * @return null - */ - static public function init($opts=null) - { - // Make sure a session is started at all time. For tokens, some cache, user data, etc. - if (!session_id()) { - session_start(); - } - - // Get the latest configuration - $default_action = isset($opts['default_action']) ? $opts['default_action'] : ''; - $default_section_id = isset($opts['default_section']) ? $opts['default_section'] : 0; - $default_lang = isset($opts['default_lang']) ? $opts['default_lang'] : 'fr'; - - // Load the action or section from the $_GET - $action = isset($_GET['action']) ? filter_input(INPUT_GET, 'action', FILTER_SANITIZE_STRING) : $default_action; - $section_id = isset($_GET['s']) ? filter_input(INPUT_GET, 's', FILTER_SANITIZE_STRING) : $default_section_id; - - // Set up the language and the required CSV file - $lang = isset($_GET['lang']) ? filter_input(INPUT_GET, 'lang', FILTER_SANITIZE_STRING) : $default_lang; - $l = Charcoal_L10n::get(); - $l->set_lang($lang); - $l->add_resource_csv('boilerplate', $lang); - - if($section_id) { - // By section - $section_loader = new Charcoal_Object_Loader('CMS_Section'); - - $section = $section_loader->{$section_id}; - - if($section->template) { - // What to do? - } - - $tpl = Charcoal_Template::get($section->template); - - // Section is already loaded, let's tell the controller about it. - $tpl->controller()->set_section( $section ); - echo $tpl->render(); - } - else if($action) { - // By action - \Charcoal::exec($action, $_REQUEST); - } - else { - // By nothing (404 page not found). This should never happen - die('404'); - } - } -} diff --git a/www/modules/boilerplate/code/boilerplate.template.controller.php b/www/modules/boilerplate/code/boilerplate.template.controller.php deleted file mode 100644 index 8e06904..0000000 --- a/www/modules/boilerplate/code/boilerplate.template.controller.php +++ /dev/null @@ -1,263 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -/** -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Objects -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Boilerplate_Template_Controller extends Charcoal_Template_Controller -{ - /** - * Keep a copy of the current section - */ - private $_section; - /** - * Keep a copy of the Section Object loader - * @var \Charcoal_Object_Loader - */ - private $_sections_loader; - /** - * Keep a copy of the Template loader - * @var \Charcoal\Template_Loader - */ - private $_templates_loader; - /** - * Keep a copy of the Widget Loader - * @var \Charcoal\Widget_Loader - */ - private $_widgets_loader; - - /** - * Get the current project's main module - * - * @return string - */ - public function module() - { - return 'boilerplate'; - } - - - /** - * Sets the section to something when there is - * no "s" get parameter and we still wanna use this - * - * @see Cocqsac_module::init() - * @param $section CMS_Section Object - * @return this (chainable) - */ - public function set_section( $section ) - { - $this->_section = $section; - - return $this; - } - - - /** - * Get the current - * (Previously, this would have been `Charcoal::$vars['section']`) - * - * @return CMS_Section - */ - public function section() - { - if(isset($this->_section)) { - return $this->_section; - } - - $section_id = isset($_GET['s']) ? filter_input(INPUT_GET, 's', FILTER_SANITIZE_STRING) : ''; - $section = \Charcoal::obj('CMS_Section'); - if($section_id) { - $section->load($section_id); - } - $this->_section = $section; - return $section; - } - - /** - * Get a `CMS_Section` Object Loader - * - * - Use "ident" as a key, so it can be called with {{sections.ident}} - * - Cache the result, so this almost never touches the DB. - * - * ## The `\Charcoal_Object_Loader` object - * Read the documentation on `\Charcoal_Object_Loader` for more details. - * In short, it allows to call the objects with `->texts()->ident;` // `{{sections.ident}}` to return (and load on-the-fly, if required) - * the `CMS_Text` object with the `ident` "ident". - * - * @return \Charcoal\Object_Loader - * @see CMS_Section - */ - public function sections() - { - if(!$this->_sections_loader) { - // Load the object, if it was not. - $this->_sections_loader = new \Charcoal_Object_Loader('CMS_Section', 'ident', 'cache'); - } - - return $this->_sections_loader; - } - - /** - * Get a "CMS_Text" Object Loader - * - Use "ident" as a key, so it can be called with {{sections.ident}} - * - Cache the result, so this almost never touches the DB. - * - * ## The `\Charcoal_Object_Loader` object - * Read the documentation on `\Charcoal_Object_Loader` for more details. - * In short, it allows to call the objects with `->sections()->ident;` to return (and load on-the-fly, if required) - * the `CMS_Section` object with the `ident` "ident". - * - * @return \Charcoal\Object_Loader - * @see CMS_Text - */ - public function texts() - { - if(!$this->_texts_loader) { - // Load the (text loader) object, if it was not. - $this->_texts_loader = new \Charcoal_Object_Loader('CMS_Text', 'ident', 'cache'); - } - - return $this->_texts_loader; - } - - /** - * Get the latest Config instance from DB / cache - * - * @return Config - */ - public function cfg() - { - return Boilerplate_Config::get_latest(); - } - - /** - * Return the base URL - * Previously, this would have been `Charcoal::$config['URL']` (which still works) - * - * @return string - */ - public function URL() - { - return \Charcoal::$config['URL']; - } - - /** - * Return the current URL - * - * @return string - */ - public function current_url() - { - return Charcoal::$config['HTTP_MODE'] . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; - } - - /** - * Test templating. - * - * Allow to hide content under dev_mode - * - * @return boolean - */ - public function dev_mode() - { - return false; - } - - /** - * Returns the current language (2-character / ISO-639-1) - * - * @return string - */ - public function lang() - { - return _l(); - } - - /** - * Assets getters - * - * This method returns an array of lambda functions, which in turn takes the argument - * - * ## Using with mustache - * In short, this allows to get an asset with the following mustache tag: - * - `{{#assets.images}}test.png{{/assets.images}} - * - * ## Asset types - * - images - * - styles - * - scripts - * - fonts - * - files - * Read the documentation on \Charcoal\Asset for more details on how assets are loaded relative to the filesystem. - * - * @return array - * @see \Charcoal\Asset - */ - public function assets($asset_mode='url') - { - $ret = [ - 'images' => function($txt) use ($asset_mode) { - return new \Charcoal\Asset('images', $txt, $asset_mode); - }, - 'styles' => function($txt) use ($asset_mode) { - return new \Charcoal\Asset('styles', $txt, $asset_mode); - }, - 'scripts' => function($txt) use ($asset_mode) { - return new \Charcoal\Asset('scripts', $txt, $asset_mode); - }, - 'fonts' => function($txt) use ($asset_mode) { - return new \Charcoal\Asset('fonts', $txt, $asset_mode); - }, - 'files' => function($txt) use ($asset_mode) { - return new \Charcoal\Asset('files', $txt, $asset_mode); - } - ]; - - return $ret; - } - - /** - * Get the proper name of the template, usable for css class. - * - * (Replace invalid characters, such as a dot.) - * - * @return string - */ - public function template_class() - { - $token = $this->section()->template; - $search = ["boilerplate_"]; - $replace = [""]; - $token = str_replace(".", "_", $token); - - if ( is_numeric($token) ) { - $token = preg_replace('/\D+/', '', $token); - } - else { - $token = str_replace($search, $replace,$token); - $token = preg_replace('/[^\w-]/', '', strtolower($token)); - } - - return $token; - } -} diff --git a/www/modules/boilerplate/code/template.controller.boilerplate.contact.php b/www/modules/boilerplate/code/template.controller.boilerplate.contact.php deleted file mode 100644 index b39aba8..0000000 --- a/www/modules/boilerplate/code/template.controller.boilerplate.contact.php +++ /dev/null @@ -1,29 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -/** -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Controllers -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Template_Controller_Boilerplate_Contact extends Boilerplate_Template_Controller -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/code/template.controller.boilerplate.content.php b/www/modules/boilerplate/code/template.controller.boilerplate.content.php deleted file mode 100644 index 1198b5b..0000000 --- a/www/modules/boilerplate/code/template.controller.boilerplate.content.php +++ /dev/null @@ -1,29 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -/** -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Controllers -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Template_Controller_Boilerplate_Content extends Boilerplate_Template_Controller -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/code/template.controller.boilerplate.home.php b/www/modules/boilerplate/code/template.controller.boilerplate.home.php deleted file mode 100644 index 045523f..0000000 --- a/www/modules/boilerplate/code/template.controller.boilerplate.home.php +++ /dev/null @@ -1,29 +0,0 @@ - -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ - - -/** -* @category Boilerplate -* @package Boilerplate.Objects -* @subpackage Controllers -* -* @author Mathieu Ducharme -* @copyright 2014 Locomotive -* @version 2014-10-01 -* @link http://locomotive.ca -* @since Version 2014-10-01 -*/ -class Template_Controller_Boilerplate_Home extends Boilerplate_Template_Controller -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/boilerplate.config.json b/www/modules/boilerplate/config/boilerplate.config.json deleted file mode 100644 index 01a14b8..0000000 --- a/www/modules/boilerplate/config/boilerplate.config.json +++ /dev/null @@ -1,350 +0,0 @@ -{ - "config_type":"object", - "config_version":1, - - "module":"boilerplate", - - "name":{ - "en":"Configuration", - "fr":"Configuration" - }, - "description":{ - "en":"", - "fr":"" - }, - "long_description":{ - "en":"", - "fr":"" - }, - "notes":{ - "en":"", - "fr":"" - }, - - "revision":true, - - "properties":{ - "id":{ - "type":"id" - }, - "google_analytics":{ - "type":"string", - "label":{ - "fr":"ID Google Analytics", - "en":"Google Analytics ID" - }, - "notes":{ - "fr":"Ex: UA-XXXXXX-X", - "en":"Ex: UA-XXXXXX-X" - } - }, - - "default_section":{ - "type":"object", - "obj_type":"cms_section", - "label":{ - "en":"Default section", - "fr":"Section par défaut" - }, - "description":{ - "en":"Default section is loaded when the default URL (without any section) is reached, or usually when the logo is clicked. Typically, this should be a \"Home\" section.", - "fr":"La section par défaut est chargée lorsqu'un utilisateur atteint l'URL par défaut (sans paramètre de section). Est également, à l'habitude, la section qui est chargée lorsque le logo est cliqué. Typiquement, ce devrait être une section \"Accueil\"." - }, - "input_type":"tree_select", - "input_options":{ - "tree_select":{ - "tree_property":"master", - "order_property":"position" - } - } - }, - "default_lang":{ - "type":"lang", - "label":{ - "en":"Default language", - "fr":"Langage par défaut" - } - }, - "contact_address":{ - "type":"string", - "l10n":true - }, - "contact_address2":{ - "type":"string", - "l10n":true - }, - "contact_city":{ - "type":"string", - "l10n":true - }, - "contact_postal_code":{ - "type":"string" - }, - "contact_province":{ - "type":"string", - "l10n":true - }, - "contact_country":{ - "type":"string" - }, - "contact_phone":{ - "type":"phone" - }, - "contact_phone2":{ - "type":"phone" - }, - "contact_fax":{ - "type":"phone" - }, - "contact_email":{ - "type":"email" - }, - "contact_lat":{ - "type":"float" - }, - "contact_lon":{ - "type":"float" - }, - - "social_facebook_url":{ - "type":"string" - }, - "social_twitter_url":{ - "type":"string" - }, - "social_google_url":{ - "type":"string" - }, - "social_linkedin_url":{ - "type":"string" - }, - "social_instagram_url":{ - "type":"string" - }, - "social_flickr_url":{ - "type":"string" - }, - "social_youtube_url":{ - "type":"string" - }, - "meta_default_title":{ - "type":"string", - "l10n":true - }, - "meta_title_prefix":{ - "type":"string", - "l10n":true - }, - "meta_title_suffix":{ - "type":"string", - "l10n":true - }, - "meta_default_description":{ - "type":"text", - "l10n":true - }, - - "meta_default_image":{ - "type":"image", - "upload_path":"uploads/cfg/meta_default_image/" - }, - "meta_default_keywords":{ - "type":"string", - "multiple":true, - "l10n":true, - "notes":{ - "en":"", - "fr":"À moins d'avis professionnel disant le contraire, les mots-clés par défaut devraient être laissés vide." - } - }, - "signature_text":{ - "type":"string", - "l10n":true, - "label":{ - "en":"Signature text", - "fr":"Texte de la signature" - } - }, - "signature_title":{ - "type":"string", - "l10n":true, - "label":{ - "en":"Signature title", - "fr":"Titre de la signature" - }, - "description":{ - "en":"This is mainly used for SEO purpose.", - "fr":"Le titre est principalement utilisé à des fins de référencement / SEO." - } - }, - "signature_url":{ - "type":"url", - "l10n":true, - "label":{ - "en":"Signature URL", - "fr":"Lien de la signature" - } - } - }, - - "data":{ - - }, - - "sources":{ - "default":{ - "table":"boilerplate_config" - } - }, - "default_source" : "default", - - "lists":{ - "boilerplate_config":{ - "label":{ - "fr":"Liste des sections", - "en":"Section list" - }, - "description":{ - - }, - "properties":[ - - ], - "orders":{ - "position":{ - "property":"position", - "mode":"asc" - } - } - } - }, - "default_list":"boilerplate_config", - - "forms":{ - "boilerplate_config":{ - "label":{ - "en":"Configuration", - "fr":"Configuration" - }, - "groups":{ - "analytics":{ - "label":{ - "en":"Analytics", - "fr":"Outils d'analyse" - }, - "properties":[ - "google_analytics" - ] - }, - "content":{ - "label":{ - "en":"Default content settings", - "fr":"Configuration de contenu par défaut" - }, - "properties":[ - "default_section", - "default_lang" - ] - }, - "social":{ - "label":{ - "en":"Social Links", - "fr":"Liens sociaux" - }, - "properties":[ - "social_facebook_url", - "social_twitter_url", - "social_google_url", - "|", - "social_linkedin_url", - "|", - "social_instagram_url", - "social_flickr_url", - "|", - "social_youtube_url" - ], - "properties_options":{}, - "priority":30 - }, - "contact":{ - "label":{ - "en":"Meta-informations", - "fr":"Informations méta" - }, - "l10n":true, - "l10n_options":{ - "mode":"loop_group" - }, - "properties":[ - "contact_address", - "contact_address2", - "contact_city", - "contact_postal_code", - "contact_province", - "contact_country", - "|", - "contact_phone", - "contact_phone2", - "contact_fax", - "|", - "contact_email" - ], - "properties_options":{}, - "priority":40 - }, - "meta":{ - "label":{ - "en":"Meta-informations", - "fr":"Informations méta" - }, - "l10n":true, - "l10n_options":{ - "mode":"loop_group" - }, - "properties":[ - "meta_default_title", - "meta_title_prefix", - "meta_title_suffix", - "meta_default_description", - "meta_default_keywords", - "meta_default_image" - ], - "properties_options":{}, - "priority":50 - }, - "signature":{ - "label":{ - "en":"Signature", - "fr":"Signature" - }, - "l10n":true, - "l10n_options":{ - "mode":"loop_group" - }, - "properties":[ - "signature_text", - "signature_url", - "signature_title" - - ], - "priority":60 - } - } - } - }, - "default_form":"boilerplate_config", - - "dashboards":{ - - }, - - "widgets": { - - }, - - "patterns":{ - - }, - "default_pattern":"default" - -} diff --git a/www/modules/boilerplate/config/boilerplate.template.controller.json b/www/modules/boilerplate/config/boilerplate.template.controller.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/boilerplate.template.controller.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/cms.contact.json b/www/modules/boilerplate/config/cms.contact.json deleted file mode 100644 index 0e0dcd2..0000000 --- a/www/modules/boilerplate/config/cms.contact.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/cms.section.json b/www/modules/boilerplate/config/cms.section.json deleted file mode 100644 index 5867f56..0000000 --- a/www/modules/boilerplate/config/cms.section.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "url_options":{ - "rewrite_pattern":{ - "fr":"fr/{{:id}}/{{title:url}}", - "en":"en/{{:id}}/{{title:url}}" - } - } -} diff --git a/www/modules/boilerplate/config/newsletter.campaign.json b/www/modules/boilerplate/config/newsletter.campaign.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/newsletter.campaign.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/newsletter.subscription.json b/www/modules/boilerplate/config/newsletter.subscription.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/newsletter.subscription.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/template.controller.boilerplate.contact.json b/www/modules/boilerplate/config/template.controller.boilerplate.contact.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/template.controller.boilerplate.contact.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/template.controller.boilerplate.content.json b/www/modules/boilerplate/config/template.controller.boilerplate.content.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/template.controller.boilerplate.content.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/www/modules/boilerplate/config/template.controller.boilerplate.home.json b/www/modules/boilerplate/config/template.controller.boilerplate.home.json deleted file mode 100644 index 4eb7c3f..0000000 --- a/www/modules/boilerplate/config/template.controller.boilerplate.home.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file