diff --git a/.codeclimate.yml b/.codeclimate.yml
index 2447a597a..6c2241653 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -60,3 +60,7 @@ exclude_patterns:
- "e107_handlers/phpthumb/**/*"
- "e107_handlers/xmlrpc/**/*"
- "e107_web/**/*"
+- "e107_tests/tests/**/*"
+# Ignore symlinks that are allowed to be broken due to local configurations
+- "e107_tests/lib/ci/salt/pillars/config.sls"
+- "e107_tests/lib/ci/salt/pillars/config-local.sls"
diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md
similarity index 69%
rename from CONTRIBUTING.md
rename to .github/CONTRIBUTING.md
index 933e1e6df..ef1430e22 100644
--- a/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,27 +1,76 @@
-# e107 v2
+# Contributing to e107
[](https://gitter.im/e107inc/e107?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-## How to contribute
+_Firstly, thank you for taking the time to contribute to e107!_
-Contributing to e107 is quite easy if you keep in mind the following tips, to avoid some pitfalls.
+By following these guidelines, you can help us make e107 even better.
-### The big picture
+## Getting Started
-1. You need to install git on your local computer to be able to work with github!
-2. Create a fork of e107 by clicking on the "Fork" button on the top right corner of the project page.
-3. Once the forking process has finished, click on the green "Clone or download" and copy the shown url.
-4. Clone your fork to your local computer by running the `git clone [The previously copied url]` command.
-5. Make sure, your copy "knows" from the original repo by running the following command `git remote add upstream https://github.com/e107inc/e107.git`
-6. Create a new branch for each issue you tackle by running `git checkout [branch name e.g. fix_IssueNr]`
-7. Work on your fix and add the changed file to your commit by running `git add [changed file]`
-8. Commit your work by running `git commit -m [message]` e.g. for a message: "Fixes #5432 added new method to solve this"
-9. Push your commit to the remote repo (your online fork) by running `git push -u origin [branch name]`
-10. Do NOT forget to switch the branch back to master by running `git checkout master`!
-11. Now the fix is online at Github and you can now create the push request by clicking on "Compare & pull request" on YOUR forks project page. Enter some more details to the pull request to explain what you have done and click on "Create pull request"
-12. Congratulation! You have created your first pull request!
+### The Big Picture
+
+1. You need to install [Git](https://git-scm.com/) on your local computer to be able to push and pull code to and from GitHub!
+2. Create a fork of e107 by clicking on the "Fork" button on the top right corner of [the project page](https://github.com/e107inc/e107/).
+3. Once the forking process has finished, click on the green "Clone or download" and copy the URL shown.
+4. Clone your fork to your local computer by running the following command:
+ ```
+ git clone [the previously copied url]
+ ```
+5. Make sure your copy "knows" from the original repo by running:
+ ```
+ git remote add upstream https://github.com/e107inc/e107.git
+ ```
+6. Create a new branch for each [issue](https://github.com/e107inc/e107/issues) you tackle by running:
+ ```
+ git checkout [branch name, e.g. fix-IssueNumber]
+ ```
+7. Work on your fix and add changed files to your commit by running:
+ ```
+ # One changed file
+ git add [changed file]
+
+ # All changed files
+ git add -A
+ ```
+8. Commit your work by running:
+ ```
+ git commit
+ ```
+ For the commit message, add a summary of important information such as why the proposed approach was chosen or a brief description of the bug you are resolving.
+
+ **Example:**
+
+ First line:
+ ```
+ This line is a brief summary of your change
+ ```
+
+ Starting from the third line:
+ ```
+ Please provide at least a couple sentences describing the
+ change. If necessary, please summarize decisions such as
+ why the proposed approach was chosen or what bug you are
+ attempting to solve.
+
+ Fixes: #5432
+ ```
+9. Push your commit to the origin repo (your online fork) by running:
+ ```
+ git push -u origin [branch name]
+ ```
+10. Do **not** forget to switch the branch back to master by running:
+ ```
+ git checkout master
+ ```
+11. Once the commit is on GitHub, you can create a pull request to get the changes merged into the original project by clicking on "Compare & pull request" on _your_ fork's project page.
+
+ Fill out the pull request checklist and then click on "Create pull request".
+12. Congratulations! A project maintainer will review your contribution. The maintainer may:
+ * approve and merge your changes, which means your contribution will be added to e107!
+ * provide feedback if your changes need improvement.
-### More in detail
+### More Details
##### Make sure ...
diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
new file mode 100644
index 000000000..dd84ea782
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
new file mode 100644
index 000000000..bbcbbe7d6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..267bb3e52
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,33 @@
+
+
+
+
+### Motivation and Context
+
+
+
+### Description
+
+
+### How Has This Been Tested?
+
+
+
+
+
+### Types of Changes
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Performance enhancement (non-breaking change which improves efficiency)
+- [ ] Code cleanup (non-breaking change which makes code smaller or more readable)
+- [ ] Breaking change (fix or feature that would cause existing functionality to change)
+- [ ] Documentation (a change to man pages or other documentation)
+
+### Checklist
+
+
+- [ ] My code adheres to the e107 [code style standard](https://github.com/e107inc/e107/wiki/e107-Coding-Standard).
+- [ ] I have read the [**contributing** document](https://github.com/e107inc/e107/blob/master/.github/CONTRIBUTING.md).
+- [ ] I have added [tests](https://github.com/e107inc/e107/tree/master/e107_tests) to cover my changes.
+- [ ] All new and existing tests pass.
\ No newline at end of file
diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 000000000..b0d8b5e23
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,10 @@
+ignore:
+ - "e107_tests/**/*"
+
+comment:
+ layout: "reach, diff, flags, files"
+ behavior: default
+ require_changes: false
+ require_base: false
+ require_head: true
+ after_n_builds: 1
\ No newline at end of file
diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml
new file mode 100644
index 000000000..19a02fbe5
--- /dev/null
+++ b/.github/workflows/test-unit.yml
@@ -0,0 +1,126 @@
+name: Unit Tests
+
+on:
+ push:
+ pull_request:
+
+env:
+ MYSQL_DATABASE: app
+ MYSQL_ROOT_PASSWORD: 'Database Password for Continuous Integration'
+ CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
+
+jobs:
+ test:
+ strategy:
+ fail-fast: false
+ matrix:
+ interpreter:
+ - image: php:5.6
+ - image: php:7.1
+ - image: php:7.2
+ - image: php:7.3
+ - image: php:7.4
+ db:
+ - image: mysql:5.5
+ - image: mysql:5.6
+ - image: mysql:5.7
+ - image: mariadb:10.4
+ runs-on: ubuntu-latest
+ container:
+ image: ${{ matrix.interpreter.image }}
+ services:
+ db:
+ image: ${{ matrix.db.image }}
+ env:
+ MYSQL_ROOT_PASSWORD: 'Database Password for Continuous Integration'
+ MYSQL_DATABASE: 'app'
+ options: >-
+ --health-cmd "mysqladmin ping"
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Update operating system software repository
+ run: apt-get update
+
+ - name: Install operating system dependencies
+ run: apt-get install -y git zip libzip-dev
+
+ - name: PECL zip
+ run: docker-php-ext-install zip
+
+ - name: PECL xdebug
+ run: |
+ if [ $(php -r 'printf(version_compare(PHP_VERSION, "7.0.0", ">=") ? 1 : 0);') = '1' ]
+ then
+ pecl install xdebug
+ else
+ pecl install xdebug-2.5.5
+ fi
+ docker-php-ext-enable xdebug
+
+ - name: Install PHP pdo_mysql extension
+ run: docker-php-ext-install pdo_mysql
+
+ - name: Install Composer
+ run: curl -sSL https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin/ --filename=composer
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+ working-directory: ./e107_tests/
+
+ - name: Download Git submodule dependencies
+ run: git submodule update --init --recursive --remote
+
+ - name: Install the CI test configuration file
+ run: cp ./e107_tests/lib/ci/config.ci.yml ./e107_tests/config.yml
+
+ - name: Download Code Climate test reporter
+ run: |
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
+ chmod +x ./cc-test-reporter
+ working-directory: ./e107_tests/vendor/bin/
+ continue-on-error: true
+
+ - name: Code Climate report before build
+ run: ./e107_tests/vendor/bin/cc-test-reporter before-build
+ continue-on-error: true
+
+ - name: Run unit tests
+ run: |
+ if [ $(php -r 'printf(extension_loaded("xdebug") ? 1 : 0);') = '1' ]
+ then
+ php ./vendor/bin/codecept run unit --steps --debug --coverage --coverage-xml #--coverage-html
+ else
+ echo "Warning: xdebug not available; skipping coverage..."
+ php ./vendor/bin/codecept run unit --steps --debug
+ fi
+ working-directory: ./e107_tests/
+
+ - name: Send code coverage report to Code Climate
+ run: |
+ ./e107_tests/vendor/bin/cc-test-reporter format-coverage \
+ --input-type clover \
+ --output ./e107_tests/tests/_output/coverage/codeclimate.json \
+ ./e107_tests/tests/_output/coverage.xml
+ ./e107_tests/vendor/bin/cc-test-reporter upload-coverage \
+ --input ./e107_tests/tests/_output/coverage/codeclimate.json
+ continue-on-error: true
+
+ - name: Send code coverage report to Codecov
+ uses: codecov/codecov-action@v1
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ file: ./e107_tests/tests/_output/coverage.xml
+ flags: unit-tests
+ name: ${{ matrix.interpreter.image }}-${{ matrix.db.image }}
+ continue-on-error: true
+
+ - name: Upload test output
+ uses: actions/upload-artifact@v1
+ with:
+ name: tests_output
+ path: ./e107_tests/tests/_output/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..3863860f6
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "e107_tests/lib/cpaneluapi"]
+ path = e107_tests/lib/cpaneluapi
+ url = https://github.com/N1ghteyes/cpanel-UAPI-php-class.git
+ branch = master
diff --git a/README.md b/README.md
index 06e2f3859..fd2d2864d 100644
--- a/README.md
+++ b/README.md
@@ -1,78 +1,105 @@
-## e107 [Content Management System][1] (CMS) - v2
+[](https://e107.org)
+=================
-[](https://gitter.im/e107inc/e107?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](https://github.com/e107inc/e107/releases)
+[](https://github.com/e107inc/e107/actions)
+[](https://codeclimate.com/github/e107inc/e107/test_coverage)
+[](https://gitter.im/e107inc/e107?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-### What is e107?
-e107 is a free (open-source) content management system which allows you to easily manage and publish your content online. Developers can save time in building websites and powerful online applications. Users can avoid programming completely! Blogs, Websites, Intranets - e107 does it all.
+**[e107][1]** is a free and open-source content management system (CMS) which allows you to manage and publish your content online with ease. Developers can save time in building websites and powerful online applications. Users can avoid programming completely! Blogs, websites, intranets – e107 does it all.
-### Requirements
+## Table of Contents
- #### Minimum
+ * [e107 Content Management System](README.md)
+ * [Table of Contents](#table-of-contents)
+ * [Requirements](#requirements)
+ * [Minimum](#minimum)
+ * [Recommended](#recommended)
+ * [Installation](#installation)
+ * [Standard Installation](#standard-installation)
+ * [Git Installation (developer version)](#git-installation-developer-version)
+ * [Reporting Bugs](#reporting-bugs)
+ * [Contributing to Development](#contributing-to-development)
+ * [Donations](#donations)
+ * [Support](#support)
+ * [License](#license)
- * A Web-Server (Apache or Microsoft IIS) running PHP v5.6 or higher and MySQL 4.x or 5.x.
- * FTP access to your webserver and an FTP program such as Filezilla
- * Username/Password to your MySQL Database
+## Requirements
- #### Recommended
+ ### Minimum
- * A Linux based Webserver running Apache 2.x, PHP 7.x and MySQL 5.x (LAMP)
- * A registered Domain Name
- * Access to a Server Control Panel (such as cPanel)
+ * A web server (Apache or Microsoft IIS) running PHP 5.6 or newer
+ * MySQL 4.x or newer, or MariaDB
+ * FTP access to your web server and an FTP client (such as FileZilla)
+ * Username and password to your MySQL database
+
+ ### Recommended
+
+ * Apache 2.2 or newer on Linux with PHP 7.0 or newer
+ * MySQL 5.x or newer, or MariaDB
+ * A registered domain name
+ * Access to a server control panel (such as cPanel)
-### Standard Installation
+## Installation
-* Unzip/Extract the compressed file onto your server.
-* Point your browser to the *http://localhost/YOUR FOLDER/install.php* (depending on your webserver setup)
-* Follow the installation wizard
+### Standard Installation
+
+1. [Download e107](https://e107.org/download).
+2. Unzip/Extract the compressed file onto your desired web root.
+ This is often a folder called `public_html`.
+3. Point your browser to the `install.php` script (e.g., `https://example.com/subfolder/install.php`)
+4. Follow the installation wizard in your browser.
### Git Installation (developer version)
-* Run the following commands ( where 'youraccount' is the folder above your public_html folder and xxx:xxx is the default owner for your files - this can be found using FileZilla and FTP)
-```
- cd youraccount
- git clone https://github.com/e107inc/e107.git public_html
- chown -R xxx:xxx public_html
-```
-* Point your browser to the *http://localhost/YOUR FOLDER/install.php* (depending on your webserver setup)
-* Follow the installation wizard
+1. Run the following commands, replacing '~' with your document root (the parent of `public_html`) and xxx:xxx is the intended owner of your e107 files.
+ ```
+ cd ~
+ git clone https://github.com/e107inc/e107.git public_html
+ chown -R xxx:xxx public_html
+ ```
+2. Point your browser to the `install.php` script (e.g., `https://example.com/subfolder/install.php`)
+3. Follow the installation wizard in your browser.
-### Reporting Bugs
+## Reporting Bugs
-Be sure you are using the most recent version prior to reporting an issue.
-You may report any bugs or feature requests on GitHub (https://github.com/e107inc/e107/issues)
+Be sure you are using the most recent version of e107 prior to reporting an issue.
+You may report any bugs and make feature requests [e107's GitHub Issues page](https://github.com/e107inc/e107/issues).
-### Pull-Requests
+## Contributing to Development
-* Please submit 1 pull-request for each Github #issue you may work on.
-* Make sure that only the lines you have changed actually show up in a file-comparison (diff) ie. some text-editors alter every line so this should be avoided.
-* Make sure you are using rebase on your local .git/config file.
-ie. [branch "master"]
- rebase = true`
-* Here's a small tutorial to give you a start on [CONTRIBUTING](CONTRIBUTING.md)
+* Please submit 1 pull request for each GitHub issue you work on.
+* Make sure that only the lines you have changed actually show up in a file-comparison (diff).
+ Some text editors alter every line; this should be avoided.
+* It is recommended to configure `git pull` to rebase on the master branch by default to avoid unnecessary merge commits. You can set this up in your copy of the repo's `.git/config` file like so:
+ ```
+ [branch "master"]
+ rebase = true
+ ```
+* See the [CONTRIBUTING](.github/CONTRIBUTING.md) document for a tutorial on getting started.
-### Donations
-If you like e107 and wish to help it to improve - please consider making a small donation.
+## Donations
+If you like e107 and wish to help it to improve, please consider making a small donation.
* Bitcoin address: 18C7W2YvkzSjvPoW1y46PjkTdCr9UzC3F7
-* Paypal: donate (at) e107.org
+* PayPal: donate (at) e107.org
-### Support
-* http://e107help.org
+## Support
+* https://e107help.org
-### License
+## License
-* e107 is released under the terms and conditions of the GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
+e107 is released under the terms and conditions of the GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
- [1]: http://e107.org
- [2]: http://www.e107.org
+ [1]: https://e107.org
\ No newline at end of file
diff --git a/e107_handlers/e_db_interface.php b/e107_handlers/e_db_interface.php
index cef4b249f..01ebd58ec 100644
--- a/e107_handlers/e_db_interface.php
+++ b/e107_handlers/e_db_interface.php
@@ -352,6 +352,11 @@
function backup($table='*', $file='', $options=null);
+ /**
+ * TODO: Document this method
+ */
+ public function resetTableList();
+
/**
* Return a filtered list of DB tables.
*
@@ -386,6 +391,18 @@
+ /**
+ * XXX: e_db_pdo and e_db_mysql have differing implementations of this method.
+ * @param string $oldtable
+ * @param string $newtable
+ * @param bool $drop
+ * @param bool $data
+ * @return bool|int|PDOStatement|resource
+ */
+ public function copyTable($oldtable, $newtable, $drop = false, $data = false);
+
+
+
/**
* Drop/delete table and all it's data
* @param string $table name without the prefix
diff --git a/e107_handlers/e_db_legacy_trait.php b/e107_handlers/e_db_legacy_trait.php
index f0634c6c0..aa599cec7 100644
--- a/e107_handlers/e_db_legacy_trait.php
+++ b/e107_handlers/e_db_legacy_trait.php
@@ -38,11 +38,6 @@
public function db_Fetch($type = null)
{
- if (defined('e_LEGACY_MODE') && !is_int($type))
- {
- return $this->fetch('both');
- }
-
return $this->fetch($type);
}
diff --git a/e107_handlers/e_db_pdo_class.php b/e107_handlers/e_db_pdo_class.php
index 38fda0c5d..a24072773 100644
--- a/e107_handlers/e_db_pdo_class.php
+++ b/e107_handlers/e_db_pdo_class.php
@@ -2295,44 +2295,56 @@ class e_db_pdo implements e_db
return false;
}
- if($fields === '*')
- {
- $fields = $this->db_FieldList($table);
- $unique = $this->_getUnique($table);
+ for ($retries = 0; $retries < 3; $retries ++) {
+ list($fieldList, $fieldList2) = $this->generateCopyRowFieldLists($table, $fields);
- $flds = array();
- // randomize fields that must be unique.
- foreach($fields as $fld)
- {
- if(isset($unique[$fld]))
- {
- $flds[] = $unique[$fld] === 'PRIMARY' ? 0 : "'rand-".rand(0,999)."'"; // keep it short.
- continue;
- }
-
- $flds[] = $fld;
+ if (empty($fieldList)) {
+ $this->mysqlLastErrText = "copyRow \$fields list was empty";
+ return false;
}
- $fieldList = implode(",", $fields);
- $fieldList2 = implode(",", $flds);
+ $beforeLastInsertId = $this->lastInsertId();
+ $query = "INSERT INTO " . $this->mySQLPrefix . $table .
+ "(" . $fieldList . ") SELECT " .
+ $fieldList2 .
+ " FROM " . $this->mySQLPrefix . $table .
+ " WHERE " . $args;
+ $id = $this->gen($query);
+ $lastInsertId = $this->lastInsertId();
+ if ($beforeLastInsertId !== $lastInsertId) break;
}
- else
- {
- $fieldList = $fields;
- $fieldList2 = $fieldList;
- }
-
- if(empty($fieldList))
- {
- $this->mysqlLastErrText = "copyRow \$fields list was empty";
- return false;
- }
-
- $id = $this->gen("INSERT INTO ".$this->mySQLPrefix.$table."(".$fieldList.") SELECT ".$fieldList2." FROM ".$this->mySQLPrefix.$table." WHERE ".$args);
- $lastInsertId = $this->lastInsertId();
return ($id && $lastInsertId) ? $lastInsertId : false;
+ }
+ /**
+ * Determine before and after fields for a table
+ * @param $table string Table name, without the prefix
+ * @param $fields string Field list in query format (i.e. separated by commas) or all of them ("*")
+ * @return array Index 0 is before and index 1 is after
+ */
+ private function generateCopyRowFieldLists($table, $fields)
+ {
+ if ($fields !== '*') return array($fields, $fields);
+
+ $fieldList = $this->db_FieldList($table);
+ $unique = $this->_getUnique($table);
+
+ $flds = array();
+ // randomize fields that must be unique.
+ foreach ($fieldList as $fld) {
+ if (isset($unique[$fld])) {
+ $flds[] = $unique[$fld] === 'PRIMARY' ? 0 :
+ "'rand-" . e107::getUserSession()->generateRandomString('***********') . "'";
+ continue;
+ }
+
+ $flds[] = $fld;
+ }
+
+ $fieldList = implode(",", $fieldList);
+ $fieldList2 = implode(",", $flds);
+ return array($fieldList, $fieldList2);
}
@@ -2796,7 +2808,7 @@ class e_db_pdo implements e_db
{
// debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
$this->connect($this->mySQLserver, $this->mySQLuser, $this->mySQLpassword);
- $this->database($this->mySQLdefaultdb);
+ $this->database($this->mySQLdefaultdb, $this->mySQLPrefix);
//$this->mySQLaccess = e107::getDb()->get_mySQLaccess();
}
}
diff --git a/e107_handlers/e_parse_class.php b/e107_handlers/e_parse_class.php
index 0f1cdcbff..dd8a93748 100644
--- a/e107_handlers/e_parse_class.php
+++ b/e107_handlers/e_parse_class.php
@@ -3830,7 +3830,7 @@ class e_parser
*/
function init()
{
- $this->domObj = new DOMDocument();
+ $this->domObj = new DOMDocument('1.0', 'utf-8');
if(defined('FONTAWESOME'))
{
@@ -5265,12 +5265,14 @@ return;
{
if(empty($html)){ return ''; }
- $html = str_replace(' ', '@nbsp;', $html); // prevent replacement of with spaces.
-
+ $html = str_replace(' ', '{E_PARSER_CLEAN_HTML_NON_BREAKING_SPACE}', $html); // prevent replacement of with spaces.
+ // Workaround for https://bugs.php.net/bug.php?id=76285
+ // Part 1 of 2
+ $html = str_replace("\n", "{E_PARSER_CLEAN_HTML_LINE_BREAK}", $html);
if(strpos($html, "
'.$html.'';
+ $html = ''.$html.'';
}
else // Full HTML page.
{
@@ -5297,24 +5299,16 @@ return;
// Set it up for processing.
- // libxml_use_internal_errors(true); // hides errors.
$doc = $this->domObj;
libxml_use_internal_errors(true);
- // @$doc->loadHTML($html);
if(function_exists('mb_convert_encoding'))
{
$html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
}
- @$doc->loadHTML($html);
+ @$doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
- // $doc->encoding = 'UTF-8';
-
- // $doc->resolveExternals = true;
-
- // $tmp = $doc->getElementsByTagName('*');
-
- $this->nodesToConvert = array(); // required.
+ $this->nodesToConvert = array(); // required.
$this->nodesToDelete = array(); // required.
$this->removedList = array();
@@ -5483,20 +5477,19 @@ return;
}
*/
- $cleaned = $doc->saveHTML($doc->documentElement); // $doc->documentElement fixes utf-8/entities issue. @see http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly
-
- $cleaned = str_replace('@nbsp;', ' ', $cleaned); // prevent replacement of with spaces. - convert back.
+ $cleaned = $doc->saveHTML($doc->documentElement); // $doc->documentElement fixes utf-8/entities issue. @see http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly
+ // Workaround for https://bugs.php.net/bug.php?id=76285
+ // Part 2 of 2
+ $cleaned = str_replace("\n", "", $cleaned);
+ $cleaned = str_replace("{E_PARSER_CLEAN_HTML_LINE_BREAK}", "\n", $cleaned);
+ $cleaned = str_replace('{E_PARSER_CLEAN_HTML_NON_BREAKING_SPACE}', ' ', $cleaned); // prevent replacement of with spaces. - convert back.
$cleaned = str_replace('{{{','{', $cleaned); // convert shortcode temporary triple-curly braces back to entities.
- $cleaned = str_replace('}}}','}', $cleaned); // convert shortcode temporary triple-curly braces back to entities.
+ $cleaned = str_replace('}}}','}', $cleaned); // convert shortcode temporary triple-curly braces back to entities.
- $cleaned = str_replace(array('','','','','','',''),'',$cleaned); // filter out tags.
+ $cleaned = str_replace(array('',''),'', $cleaned); // filter out tags.
-
-
- // $cleaned = html_entity_decode($cleaned, ENT_QUOTES, 'UTF-8');
-
return trim($cleaned);
}
diff --git a/e107_handlers/file_class.php b/e107_handlers/file_class.php
index b9c889832..a4ac7e58d 100644
--- a/e107_handlers/file_class.php
+++ b/e107_handlers/file_class.php
@@ -1664,10 +1664,16 @@ class e_file
}
-
-
-
- public function unzipGithubArchive($url='core')
+ /**
+ * Download and extract a zipped copy of e107
+ * @param string $url "core" to download the e107 core from Git master or
+ * a custom download URL
+ * @param string $destination_path The e107 root where the downloaded archive should be extracted,
+ * with a directory separator at the end
+ * @return array|bool FALSE on failure;
+ * An array of successful and failed path extractions
+ */
+ public function unzipGithubArchive($url='core', $destination_path = e_BASE)
{
switch($url)
@@ -1675,8 +1681,21 @@ class e_file
case "core":
$localfile = 'e107-master.zip';
$remotefile = 'https://codeload.github.com/e107inc/e107/zip/master';
- $excludes = array('e107-master/install.php','e107-master/favicon.ico');
- $excludeMatch = false;
+ $excludes = array(
+ 'e107-master/.codeclimate.yml',
+ 'e107-master/.editorconfig',
+ 'e107-master/.gitignore',
+ 'e107-master/.gitmodules',
+ 'e107-master/CONTRIBUTING.md', # moved to ./.github/CONTRIBUTING.md
+ 'e107-master/LICENSE',
+ 'e107-master/README.md',
+ 'e107-master/install.php',
+ 'e107-master/favicon.ico',
+ );
+ $excludeMatch = array(
+ '/.github/',
+ '/e107_tests/',
+ );
break;
// language.
@@ -1712,18 +1731,18 @@ class e_file
$excludes[] = $zipBase;
$newFolders = array(
- $zipBase.'/e107_admin/' => e_BASE.e107::getFolder('ADMIN'),
- $zipBase.'/e107_core/' => e_BASE.e107::getFolder('CORE'),
- $zipBase.'/e107_docs/' => e_BASE.e107::getFolder('DOCS'),
- $zipBase.'/e107_handlers/' => e_BASE.e107::getFolder('HANDLERS'),
- $zipBase.'/e107_images/' => e_BASE.e107::getFolder('IMAGES'),
- $zipBase.'/e107_languages/' => e_BASE.e107::getFolder('LANGUAGES'),
- $zipBase.'/e107_media/' => e_BASE.e107::getFolder('MEDIA'),
- $zipBase.'/e107_plugins/' => e_BASE.e107::getFolder('PLUGINS'),
- $zipBase.'/e107_system/' => e_BASE.e107::getFolder('SYSTEM'),
- $zipBase.'/e107_themes/' => e_BASE.e107::getFolder('THEMES'),
- $zipBase.'/e107_web/' => e_BASE.e107::getFolder('WEB'),
- $zipBase.'/' => e_BASE
+ $zipBase.'/e107_admin/' => $destination_path.e107::getFolder('ADMIN'),
+ $zipBase.'/e107_core/' => $destination_path.e107::getFolder('CORE'),
+ $zipBase.'/e107_docs/' => $destination_path.e107::getFolder('DOCS'),
+ $zipBase.'/e107_handlers/' => $destination_path.e107::getFolder('HANDLERS'),
+ $zipBase.'/e107_images/' => $destination_path.e107::getFolder('IMAGES'),
+ $zipBase.'/e107_languages/' => $destination_path.e107::getFolder('LANGUAGES'),
+ $zipBase.'/e107_media/' => $destination_path.e107::getFolder('MEDIA'),
+ $zipBase.'/e107_plugins/' => $destination_path.e107::getFolder('PLUGINS'),
+ $zipBase.'/e107_system/' => $destination_path.e107::getFolder('SYSTEM'),
+ $zipBase.'/e107_themes/' => $destination_path.e107::getFolder('THEMES'),
+ $zipBase.'/e107_web/' => $destination_path.e107::getFolder('WEB'),
+ $zipBase.'/' => $destination_path
);
$srch = array_keys($newFolders);
@@ -1734,35 +1753,28 @@ class e_file
$error = array();
$success = array();
- // $skipped = array();
+ $skipped = array();
foreach($unarc as $k=>$v)
{
- if($this->matchFound($v['stored_filename'],$excludeMatch))
- {
- continue;
- }
-
- if(in_array($v['stored_filename'],$excludes))
+ if($this->matchFound($v['stored_filename'],$excludeMatch) ||
+ in_array($v['stored_filename'],$excludes))
{
+ $skipped[] = $v['stored_filename'];
continue;
}
$oldPath = $v['filename'];
$newPath = str_replace($srch,$repl, $v['stored_filename']);
-/*
- $success[] = $newPath;
- continue;*/
-
if($v['folder'] ==1 && is_dir($newPath))
{
// $skipped[] = $newPath. " (already exists)";
continue;
}
-
+ mkdir(dirname($newPath), 0755, true);
if(!rename($oldPath,$newPath))
{
$error[] = $newPath;
@@ -1774,9 +1786,7 @@ class e_file
}
-
- return array('success'=>$success, 'error'=>$error);
-
+ return array('success'=>$success, 'error'=>$error, 'skipped'=>$skipped);
}
diff --git a/e107_handlers/mysql_class.php b/e107_handlers/mysql_class.php
index 527e300fb..5de917a5b 100644
--- a/e107_handlers/mysql_class.php
+++ b/e107_handlers/mysql_class.php
@@ -65,7 +65,7 @@ $db_ConnectionID = NULL; // Stores ID for the first DB connection used - which s
-class e_db_mysql
+class e_db_mysql implements e_db
{
// TODO switch to protected vars where needed
public $mySQLserver;
@@ -488,7 +488,7 @@ class e_db_mysql
if ($debug == 'now')
{
- echo "
** $query
\n";
+ $this->dbg->log($query);
}
if ($debug !== FALSE || strstr($_SERVER['QUERY_STRING'], 'showsql'))
{
@@ -2607,6 +2607,13 @@ class e_db_mysql
$this->mySQLtableList = array();
$this->mySQLtableListLanguage = array();
}
+ /**
+ * @inheritDoc
+ */
+ public function resetTableList()
+ {
+ return $this->db_ResetTableList();
+ }
/**
* Legacy Alias of tables
@@ -2751,6 +2758,13 @@ class e_db_mysql
return $result;
}
+ /**
+ * @inheritDoc
+ */
+ public function copyTable($oldtable, $newtable, $drop = false, $data = false) {
+ return $this->db_CopyTable($oldtable, $newtable, $drop, $data);
+ }
+
@@ -3243,6 +3257,30 @@ class e_db_mysql
return $this->mySQLaccess;
}
+ /**
+ * @inheritDoc
+ */
+ public function setLanguage($lang)
+ {
+ $this->mySQLlanguage = $lang;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLanguage()
+ {
+ return $this->mySQLlanguage;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dropTable($table)
+ {
+ $name = $this->mySQLPrefix.strtolower($table);
+ return $this->gen("DROP TABLE IF EXISTS ".$name);
+ }
}
/**
diff --git a/e107_tests/.gitignore b/e107_tests/.gitignore
new file mode 100644
index 000000000..f6cfde8cc
--- /dev/null
+++ b/e107_tests/.gitignore
@@ -0,0 +1,13 @@
+/config.yml
+/config.local.yml
+/tests/_output/*
+/tests/_support/_generated/*
+composer.phar
+composer.lock
+/vendor/**/*
+
+error_log
+
+*.swp
+*.swo
+.idea
diff --git a/e107_tests/.htaccess b/e107_tests/.htaccess
new file mode 100644
index 000000000..c8ee4c577
--- /dev/null
+++ b/e107_tests/.htaccess
@@ -0,0 +1,2 @@
+# Prevent anyone from browsing this folder on Apache HTTP Server
+Deny from all
diff --git a/e107_tests/README.md b/e107_tests/README.md
new file mode 100644
index 000000000..2060e4cf0
--- /dev/null
+++ b/e107_tests/README.md
@@ -0,0 +1,331 @@
+e107 Test Suites
+===
+
+## Quickstart
+
+1. Check out this repository:
+ ```sh
+ # SSH option
+ git clone git@github.com:e107inc/e107.git
+
+ # HTTPS option
+ git clone https://github.com/e107inc/e107.git
+ ```
+2. Change your current working directory into your copy of the tests:
+ ```sh
+ cd e107/e107_tests
+ ```
+3. Configure the testing environment by copying [`config.sample.yml`](config.sample.yml) into `config.yml` at the root level of this repository and then editing `config.yml`. The `db` section needs to be configured for all tests while acceptance tests can be configured with one of the following deployment options:
+
+ * [**Local**](#local)
+
+ *Use if:* You develop the app locally and have a LAMP/WAMP stack that is configured to serve the app at a local path
+
+ * [**SFTP**](#sftp)
+
+ *Use if:* You regularly upload the app over SFTP (perhaps in some setup with Vagrant) and have a remote LAMP stack that serves the app
+
+ * [**cPanel**](#cpanel)
+
+ *Use if:* You have a cPanel account whose main domain is reachable and want to run tests without a staging environment
+
+ * [**Manual**](#manual)
+
+ *Use if:* You are not able to set up any of the other options
+
+4. On PHP 5.6 or newer, install dependencies with [Composer](https://getcomposer.org/):
+ ```sh
+ php -d allow_url_fopen=On $(which composer) update
+ ```
+5. Update all submodules:
+ ```sh
+ git submodule update --init --recursive --remote
+ ```
+6. Run tests:
+
+ * **All tests:**
+ ```sh
+ ./vendor/bin/codecept run
+ ```
+ * **All tests and details:**
+ ```sh
+ php -d allow_url_fopen=On -d display_errors=On ./vendor/bin/codecept run --step --debug
+ ```
+ * **All tests with code coverage report:**
+ ```sh
+ /opt/cpanel/ea-php72/root/usr/bin/php -d zend_extension=/opt/alt/php72/usr/lib64/php/modules/xdebug.so -d allow_url_fopen=On ./vendor/bin/codecept run --coverage --coverage-xml --coverage-html
+ ```
+ > **Note:** This command is specific to cPanel EasyApache PHP 7.2 and CloudLinux PHP Selector. See the "Code Coverage" section below for details.
+ * **Unit tests:**
+ ```sh
+ ./vendor/bin/codecept run unit
+ ```
+ * **Functional tests:**
+ ```sh
+ ./vendor/bin/codecept run functional
+ ```
+ * **Acceptance tests:**
+ ```sh
+ ./vendor/bin/codecept run acceptance
+ ```
+
+## Deployment
+
+With varying levels of configuration, this test harness can run reproducible tests on different environments.
+
+The default configuration is in [`config.sample.yml`](config.sample.yml). Each configuration item can be overridden in a non-version-controlled `config.yml` that you create or copy from [`config.sample.yml`](config.sample.yml). (It is optionally possible to override both configuration files with a `config.local.yml`.)
+
+In the config file, there are some base settings:
+
+* `app_path` – The path, relative or absolute, to the app intended to be tested. The deployers use the app at this path to set up the tests.
+* `deployer` – Which deployer is to be used to set up tests. See the sections below for configuration instructions for the respective deployers.
+
+Each deployer needs one or more of the following sections to be configured:
+
+* `hosting` – The credentials to log in to an all-in-one hosting control panel
+* `url` – The URL that acceptance tests will access
+* `db` – Database credentials and populator settings
+* `fs` – File transfer credentials if the app is hosted at a remote location
+
+Details on how to configure these sections can be found in [`config.sample.yml`](config.sample.yml) or further down this README.
+
+Here is a table of which sections need to be configured for which deployers:
+
+| Deployer (`deployer`) | Hosting platform (`hosting`) required? | URL (`url`) required? | Database (`db`) required? | Files (`fs`) required? |
+| --- |:---:|:---:|:---:|:---:|
+| Local (`local`) | no | yes | yes | no |
+| SFTP (`sftp`) | no | yes | yes | yes |
+| cPanel (`cpanel`) | yes | no | no | no |
+| Manual (`none`) | no | no | yes | no |
+
+### Local
+
+#### Requirements
+
+* **Local testing environment** – The app's files must be served from the same system as this test harness.
+* **Local web server** – A web server with PHP must serve the app from the same local path as the app's files.
+* **MySQL/MariaDB database** – The web server and test harness must be able to access a MySQL or MariaDB database, not necessarily a local one.
+
+#### Configuration
+
+To set up locally deployed tests, copy the file called [`config.sample.yml`](config.sample.yml) in the root folder of this repository to a new file called `config.yml` (or create a new file called `config.yml`), open `config.yml`, and input the following configuration information:
+
+```yaml
+# Path (absolute or relative) to the app intended to be tested
+# Absolute path begins with "/"; relative path does not begin with "/"
+app_path: '../'
+
+# Which deployer to use to set up tests
+deployer: 'local'
+
+# URL (with trailing slash) at which the app can be reached for acceptance tests
+url: 'http://set-this-to-your-acceptance-test-url.local/'
+
+# Only MySQL/MariaDB is supported
+db:
+
+ # Hostname or IP address; use 'localhost' for a local server
+ host: 'set-this-to-your-test-database-hostname.local'
+
+ # Port number of the server
+ port: '3306'
+
+ # Database name; must exist already
+ dbname: 'e107'
+
+ # Username; must exist already
+ user: 'root'
+
+ # Password; set to blank string for no password
+ password: ''
+```
+
+### SFTP
+
+#### Requirements
+
+* **Remote SSH server** – This is where the app's files would be sent. The SSH account needs shell access and the `rsync` command.
+* **`rsync`** – Both the client and server need the `rsync` command.
+* **`sshpass`** – The client needs the `sshpass` command only if password authentication is being used (the `fs.password` field in `config.yml`).
+* **Private key file to authenticate** – Only needed if the SSH account is authenticated by private key (`fs.privkey_path` in `config.yml` is set to the path of the private key). `fs.privkey_path` can be left blank if the SSH client configuration already has an identity file set for the remote SSH account.
+* **Web server** – A web server with PHP must serve the app from the uploaded destination.
+* **MySQL/MariaDB database** – The web server and the test harness must be able to access a MySQL or MariaDB database.
+
+#### Configuration
+
+To set up SFTP-deployed tests, copy the file called [`config.sample.yml`](config.sample.yml) in the root folder of this repository to a new file called `config.yml` (or create a new file called `config.yml`), open `config.yml`, and input the following configuration information:
+
+```yaml
+# Path (absolute or relative) to the app intended to be tested
+# Absolute path begins with "/"; relative path does not begin with "/"
+app_path: '../'
+
+# Which deployer to use to set up tests
+deployer: 'sftp'
+
+# URL (with trailing slash) at which the app can be reached for acceptance tests
+url: 'http://set-this-to-your-acceptance-test-url.local/'
+
+# Only MySQL/MariaDB is supported
+db:
+
+ # Hostname or IP address; use 'localhost' for a local server
+ host: 'set-this-to-your-test-database-hostname.local'
+
+ # Port number of the server
+ port: '3306'
+
+ # Database name; must exist already
+ dbname: 'e107'
+
+ # Username; must exist already
+ user: 'root'
+
+ # Password; set to blank string for no password
+ password: ''
+
+# Configure this section for deployers that need file upload configuration
+fs:
+
+ # Hostname or IP address to the remote destination
+ host: ''
+
+ # Port number of the file transfer server
+ port: '22'
+
+ # Username used for the file transfer
+ user: ''
+
+ # Path to the private key of the user. Takes precedence over "fs.password"
+ privkey_path: ''
+
+ # Password of the file transfer user. Ignored if "fs.privkey_path" is specified
+ password: ''
+
+ # Absolute path to where the remote web server serves "url"
+ path: ''
+```
+
+### cPanel
+
+#### Requirements
+
+* **cPanel user account** – It is recommended to use a cPanel account dedicated to testing for isolation, but the test suite runs on most typical accounts and tries not to interfere with existing data.
+* **Resolvable main domain** – The cPanel account's main domain must be resolvable to the machine running the test suite. This usually means that the domain must resolve on the Internet.
+* **MariaDB database quota** – Each run of the test suite creates one new MariaDB database and deletes it after executing the suite.
+* **Enough free disk space** – The test suite archives a copy of the app and uploads it to the cPanel account for cPanel to extract. The app, its archive form, and test resources may grow in the future, so the more free disk space, the better.
+* **Enough free inodes** – The app and test resources will take up at least a few thousand inodes and may need more in the future, so the more free inodes, the better.
+
+#### Limitations
+
+* **PHP version cannot be set** – The test suite currently does not have the ability to set custom versions of PHP for the target app directory. If the cPanel host supports multiple versions of PHP (e.g. EasyApache 4 MultiPHP, CloudLinux alt-php), they will have to be configured manually to test different PHP versions.
+* **MariaDB username character limit** – cPanel MariaDB usernames are limited to 47 characters in length, and test runs are expected to use 18 plus the length of your cPanel username plus 1.
+* **MariaDB database character limit** – cPanel MariaDB databases are limited to 64 characters in length, and test runs are expected to use 18 plus the length of your cPanel username plus 4. (cPanel double-counts underscores (`_`) and the deployer uses 2 underscores, so the visible character count is 2 less than what cPanel counts.)
+* **MariaDB remote access host `%` is preserved on crash** – The deployer adds a cPanel Remote MySQL® access host, `%`, but will forget to remove it if the test run is uncleanly aborted. Subsequent runs will not touch the `%` remote access host because the deployer would not be sure if it added `%`.
+* **cPanel max POST size** – The cPanel PHP maximum POST request size can be as low as 55MiB on some hosts. If the app's archive form exceeds this size, the upload will fail. This limit can be adjusted in the hosting provider's server-wide WHM settings.
+
+#### Configuration
+
+To set up the deployment of tests to a cPanel account, copy the file called [`config.sample.yml`](config.sample.yml) in the root folder of this repository to a new file called `config.yml` (or create a new file called `config.yml`), open `config.yml`, and input the following configuration information:
+
+```yaml
+# Path (absolute or relative) to the app intended to be tested
+# Absolute path begins with "/"; relative path does not begin with "/"
+app_path: '../'
+
+# Which deployer to use to set up tests
+deployer: 'cpanel'
+
+# Configure this section for fully automated test deployments to a hosting control panel
+hosting:
+
+ # Control panel domain without the port number
+ hostname: ''
+
+ # Control panel account username
+ username: ''
+
+ # Control panel account password
+ password: ''
+```
+
+### Manual
+
+#### Requirements
+
+* **MySQL/MariaDB database** – The test harness must be able to access a MySQL or MariaDB database because the database currently cannot be abstracted away in test code.
+* **Web server** – To run acceptance tests, a web server with PHP is needed.
+
+#### Limitations
+
+* **Acceptance tests cannot be deployed automatically** – In this manual testing mode, the app deployed for acceptance tests must be reset manually before each test. Running the entire suite at once is likely to cause failures that would not occur with an automated test deployer.
+
+#### Configuration
+
+To turn off automated acceptance test deployments, copy the file called [`config.sample.yml`](config.sample.yml) in the root folder of this repository to a new file called `config.yml` (or create a new file called `config.yml`), open `config.yml`, and input the following configuration information:
+
+```yaml
+# Path (absolute or relative) to the app intended to be tested
+# Absolute path begins with "/"; relative path does not begin with "/"
+app_path: '../'
+
+# Which deployer to use to set up tests
+deployer: 'none'
+
+# URL (with trailing slash) at which the app can be reached for acceptance tests
+url: 'http://set-this-to-your-acceptance-test-url.local/'
+
+# Only MySQL/MariaDB is supported
+db:
+
+ # Hostname or IP address; use 'localhost' for a local server
+ host: 'set-this-to-your-test-database-hostname.local'
+
+ # Port number of the server
+ port: '3306'
+
+ # Database name; must exist already
+ dbname: 'e107'
+
+ # Username; must exist already
+ user: 'root'
+
+ # Password; set to blank string for no password
+ password: ''
+
+ # If set to true, the database populator will populate the database with the dump specified in the "dump_path" key
+ # If set to false, the test database needs to be set up separately
+ # Affects all tests and modes of deployment
+ populate: true
+
+ # Path (absolute or relative) to the database dump of a testable installation of the app
+ # Absolute path begins with "/"; relative path does not begin with "/"
+ dump_path: 'tests/_data/e107_v2.1.9.sample.sql'
+```
+
+## Code Coverage
+
+You can generate code coverage reports for all PHP files in the app. Code coverage is enabled for local tests (unit and functional tests) but disabled for remote tests (acceptance tests) by default.
+
+The reports may take minutes to be generated.
+
+### Requirements
+
+* **[Xdebug](https://xdebug.org/)** – You'll have to figure out the best way to [install Xdebug](https://xdebug.org/docs/install) in your environment.
+
+### Sample Commands
+
+These commands run all tests and generate a code coverage report in HTML format and [Clover](https://bitbucket.org/atlassian/clover) XML format:
+
+* Using [cPanel EasyApache 4](https://documentation.cpanel.net/display/EA4/PHP+Home) with PHP 7.2 and Xdebug from [CloudLinux PHP Selector](https://docs.cloudlinux.com/php_selector.html):
+ ```sh
+ /opt/cpanel/ea-php72/root/usr/bin/php -d zend_extension=/opt/alt/php72/usr/lib64/php/modules/xdebug.so -d allow_url_fopen=On ./vendor/bin/codecept run --coverage --coverage-xml --coverage-html
+ ```
+* Using the Xdebug module that you installed with PECL:
+ ```sh
+ php zend_extension=/usr/local/php/modules/xdebug.so -d allow_url_fopen=On ./vendor/bin/codecept run --coverage --coverage-xml --coverage-html
+ ```
+
+### Output
+
+The generated coverage reports are stored in `./tests/_output/` relative to the tests root folder.
\ No newline at end of file
diff --git a/e107_tests/codeception.yml b/e107_tests/codeception.yml
new file mode 100644
index 000000000..9331cb6cd
--- /dev/null
+++ b/e107_tests/codeception.yml
@@ -0,0 +1,32 @@
+paths:
+ tests: tests
+ output: tests/_output
+ data: tests/_data
+ support: tests/_support
+ envs: tests/_envs
+actor_suffix: Tester
+bootstrap: _bootstrap.php
+coverage:
+ enabled: true
+ include:
+ - '%app_path%/*.php'
+ - '%app_path%/e107_admin/**/*.php'
+ - '%app_path%/e107_core/**/*.php'
+ - '%app_path%/e107_handlers/**/*.php'
+ - '%app_path%/e107_images/**/*.php'
+ - '%app_path%/e107_plugins/**/*.php'
+ - '%app_path%/e107_themes/**/*.php'
+ - '%app_path%/e107_web/**/*.php'
+params:
+ - lib/config.php
+extensions:
+ enabled:
+ - Codeception\Extension\RunFailed
+modules:
+ enabled:
+ - \Helper\DelayedDb:
+ dsn: 'mysql:host=%db.host%;port=%db.port%;dbname=%db.dbname%'
+ user: '%db.user%'
+ password: '%db.password%'
+ populate: '%db.populate%'
+ dump: '%db.dump_path%'
diff --git a/e107_tests/composer.json b/e107_tests/composer.json
new file mode 100644
index 000000000..e5f767b04
--- /dev/null
+++ b/e107_tests/composer.json
@@ -0,0 +1,12 @@
+{
+ "name": "e107inc/e107-test",
+ "description": "Test harness for e107",
+ "license": "GPL-3.0-or-later",
+ "require-dev": {
+ "twig/twig": ">=1.28",
+ "codeception/codeception": "^4.0",
+ "codeception/module-asserts": "^1.1",
+ "codeception/module-db": "^1.0",
+ "codeception/module-phpbrowser": "^1.0"
+ }
+}
diff --git a/e107_tests/config.sample.yml b/e107_tests/config.sample.yml
new file mode 100644
index 000000000..6e817d19a
--- /dev/null
+++ b/e107_tests/config.sample.yml
@@ -0,0 +1,83 @@
+---
+
+# Path (absolute or relative) to the app intended to be tested
+# Absolute path begins with "/"; relative path does not begin with "/"
+app_path: '../'
+
+# Which deployer to use to set up tests. Options:
+#
+# 'none'
+# Dummy deployer that does nothing. Tests that depend on a deployer will fail.
+# 'local'
+# Use this if the acceptance test web server directly serves files from "app_path".
+# Configure the "url" and "db" sections.
+# 'sftp'
+# Deploys the files in "app_path" to an SFTP account.
+# Configure the "url", "db", and "fs" sections.
+# 'cpanel'
+# Deploys the files in "app_path" to a cPanel account's main domain.
+# Configure the "hosting" section.
+deployer: 'local'
+
+# Configure this section for fully automated test deployments to a hosting control panel
+hosting:
+
+ # Control panel domain without the port number
+ hostname: ''
+
+ # Control panel account username
+ username: ''
+
+ # Control panel account password
+ password: ''
+
+# URL (with trailing slash) at which the app can be reached for acceptance tests
+url: 'http://set-this-to-your-acceptance-test-url.local/'
+
+# Only MySQL/MariaDB is supported
+db:
+
+ # Hostname or IP address; use 'localhost' for a local server
+ host: 'set-this-to-your-test-database-hostname.local'
+
+ # Port number of the server
+ port: '3306'
+
+ # Database name; must exist already
+ dbname: 'e107'
+
+ # Username; must exist already
+ user: 'root'
+
+ # Password; set to blank string for no password
+ password: ''
+
+ # If set to true, the database populator will populate the database with the dump specified in the "dump_path" key
+ # If set to false, the test database needs to be set up separately
+ # Affects all tests and modes of deployment
+ populate: true
+
+ # Path (absolute or relative) to the database dump of a testable installation of the app
+ # Absolute path begins with "/"; relative path does not begin with "/"
+ dump_path: 'tests/_data/e107_v2.1.9.sample.sql'
+
+# Configure this section for deployers that need file upload configuration
+fs:
+
+ # Hostname or IP address to the remote destination
+ host: ''
+
+ # Port number of the file transfer server
+ port: '22'
+
+ # Username used for the file transfer
+ user: ''
+
+ # Path to the private key of the user. Takes precedence over "fs.password"
+ privkey_path: ''
+
+ # Password of the file transfer user. Ignored if "fs.privkey_path" is specified
+ password: ''
+
+ # Absolute path to where the remote web server serves "url"
+ path: ''
diff --git a/e107_tests/index.html b/e107_tests/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/lib/PriorityCallbacks.php b/e107_tests/lib/PriorityCallbacks.php
new file mode 100644
index 000000000..a66c78e3b
--- /dev/null
+++ b/e107_tests/lib/PriorityCallbacks.php
@@ -0,0 +1,46 @@
+shutdown_functions as $shutdown_function)
+ {
+ call_user_func($shutdown_function);
+ }
+ }
+
+ public function register_shutdown_function($callable)
+ {
+ $this->shutdown_functions[] = $callable;
+ }
+
+ private function __clone() {}
+
+ private function __sleep() {}
+
+ private function __wakeup() {}
+}
+
+PriorityCallbacks::instance();
\ No newline at end of file
diff --git a/e107_tests/lib/ci/config.ci.yml b/e107_tests/lib/ci/config.ci.yml
new file mode 100644
index 000000000..dd25927a2
--- /dev/null
+++ b/e107_tests/lib/ci/config.ci.yml
@@ -0,0 +1,10 @@
+---
+deployer: 'local'
+url: 'http://set-this-to-your-acceptance-test-url.local/'
+db:
+ host: 'db'
+ dbname: 'app'
+ user: 'root'
+ password: 'Database Password for Continuous Integration'
+ populate: true
+ dump_path: 'tests/_data/e107_v2.1.9.sample.sql'
diff --git a/e107_tests/lib/ci/salt/.gitignore b/e107_tests/lib/ci/salt/.gitignore
new file mode 100644
index 000000000..60923f931
--- /dev/null
+++ b/e107_tests/lib/ci/salt/.gitignore
@@ -0,0 +1,4 @@
+roster
+cache/
+logs/
+var/
diff --git a/e107_tests/lib/ci/salt/README.md b/e107_tests/lib/ci/salt/README.md
new file mode 100644
index 000000000..3fc8d162b
--- /dev/null
+++ b/e107_tests/lib/ci/salt/README.md
@@ -0,0 +1,58 @@
+# e107 Local Test Container Setup
+
+1. Launch a development container:
+
+ ```
+ lxc launch -s local images:ubuntu/focal e107-dev
+ ```
+
+2. Push your public SSH key:
+
+ ```
+ lxc file push --uid 0 --gid 0 -pv ~/.ssh/id_rsa.pub e107-dev/root/.ssh/authorized_keys
+ ```
+
+3. Install OpenSSH Server:
+
+ ```
+ lxc exec e107-dev -- apt install -y openssh-server
+ ```
+
+4. Note the IP of the container:
+
+ ```
+ E107_DEV_HOST=$(lxc exec e107-dev -- hostname -I | cut -d' ' -f1)
+ ```
+
+5. Generate the [Salt SSH](https://docs.saltstack.com/en/latest/topics/ssh/) [roster](https://docs.saltstack.com/en/latest/topics/ssh/roster.html):
+
+ ```
+ echo "e107-dev: $E107_DEV_HOST" | tee roster
+ ```
+
+6. Configure `e107_tests/config.yml` based on `e107_tests/config.sample.yml` (from the root of this repository).
+
+ For all tests:
+
+ > Set `db.dbname`, `db.user`, and `db.password` to what you want the container configuration to have.
+ >
+ > Set `db.host` to the value of `$E107_DEV_HOST`.
+
+ For acceptance tests:
+
+ > Set `deployer` to `sftp`.
+ >
+ > Set `fs.host` to the value of `$E107_DEV_HOST`.
+ >
+ > Set `fs.user` to `www-data`.
+ >
+ > Set `fs.password` to any password you want the user to have.
+ >
+ > Set `fs.path` to `/var/www/html/e107/`.
+ >
+ > Set `url` to the output of `echo "http://$E107_DEV_HOST/e107/"`
+
+7. Apply the container configuration:
+ ```
+ salt-ssh 'e107-dev' --state-output=changes state.apply e107-dev
+ ```
\ No newline at end of file
diff --git a/e107_tests/lib/ci/salt/Saltfile b/e107_tests/lib/ci/salt/Saltfile
new file mode 100644
index 000000000..3309324cf
--- /dev/null
+++ b/e107_tests/lib/ci/salt/Saltfile
@@ -0,0 +1,4 @@
+salt-ssh:
+ roster_file: roster
+ config_dir: .
+ ssh_log_file: logs/salt-ssh
diff --git a/e107_tests/lib/ci/salt/master b/e107_tests/lib/ci/salt/master
new file mode 100644
index 000000000..c4da4f0e0
--- /dev/null
+++ b/e107_tests/lib/ci/salt/master
@@ -0,0 +1,15 @@
+root_dir: ./
+
+file_roots:
+ base:
+ - states/
+
+pillar_roots:
+ base:
+ - pillars/
+
+log_file: logs/master
+
+cachedir: cache/
+
+file_ignore_glob: []
diff --git a/e107_tests/lib/ci/salt/pillars/config-local.sls b/e107_tests/lib/ci/salt/pillars/config-local.sls
new file mode 120000
index 000000000..a56149aa2
--- /dev/null
+++ b/e107_tests/lib/ci/salt/pillars/config-local.sls
@@ -0,0 +1 @@
+../../../../config.local.yml
\ No newline at end of file
diff --git a/e107_tests/lib/ci/salt/pillars/config-sample.sls b/e107_tests/lib/ci/salt/pillars/config-sample.sls
new file mode 120000
index 000000000..aea216abc
--- /dev/null
+++ b/e107_tests/lib/ci/salt/pillars/config-sample.sls
@@ -0,0 +1 @@
+../../../../config.sample.yml
\ No newline at end of file
diff --git a/e107_tests/lib/ci/salt/pillars/config.sls b/e107_tests/lib/ci/salt/pillars/config.sls
new file mode 120000
index 000000000..0ecb7253d
--- /dev/null
+++ b/e107_tests/lib/ci/salt/pillars/config.sls
@@ -0,0 +1 @@
+../../../../config.yml
\ No newline at end of file
diff --git a/e107_tests/lib/ci/salt/pillars/top.sls b/e107_tests/lib/ci/salt/pillars/top.sls
new file mode 100644
index 000000000..3616e4ad9
--- /dev/null
+++ b/e107_tests/lib/ci/salt/pillars/top.sls
@@ -0,0 +1,5 @@
+base:
+ e107-dev:
+ - config-sample
+ - config
+ - config-local
diff --git a/e107_tests/lib/ci/salt/roster b/e107_tests/lib/ci/salt/roster
new file mode 100644
index 000000000..725b6ac56
--- /dev/null
+++ b/e107_tests/lib/ci/salt/roster
@@ -0,0 +1 @@
+e107-dev: e107-dev.lxc
diff --git a/e107_tests/lib/ci/salt/states/e107-dev.sls b/e107_tests/lib/ci/salt/states/e107-dev.sls
new file mode 100644
index 000000000..fd2180195
--- /dev/null
+++ b/e107_tests/lib/ci/salt/states/e107-dev.sls
@@ -0,0 +1,71 @@
+Install LAMP stack:
+ pkg.installed:
+ - pkgs:
+ - mariadb-server
+ - python3-mysqldb
+ - php
+ - libapache2-mod-php
+ - php-mysql
+ - php-xml
+ - php-curl
+ - php-gd
+ - php-mbstring
+
+MySQL server configuration file:
+ file.managed:
+ - name: /etc/mysql/mariadb.conf.d/99-overrides.cnf
+ - source: salt://files/etc/mysql/mariadb.conf.d/99-overrides.cnf
+ - user: root
+ - group: root
+ - mode: 0644
+ - template: jinja
+
+Start and enable MySQL server daemon:
+ service.running:
+ - name: mysql
+ - enable: True
+ - watch:
+ - file: /etc/mysql/mariadb.conf.d/99-overrides.cnf
+
+Create MySQL user:
+ mysql_user.present:
+ - name: {{ salt['pillar.get']('db:user') }}
+ - host: '%'
+ - password: {{ salt['pillar.get']('db:password') }}
+ - allow_passwordless: True
+ - unix_socket: False
+
+Create MySQL database:
+ mysql_database.present:
+ - name: {{ salt['pillar.get']('db:dbname') }}
+
+Create MySQL grants:
+ mysql_grants.present:
+ - grant: ALL PRIVILEGES
+ - database: {{ salt['pillar.get']('db:dbname') }}.*
+ - user: {{ salt['pillar.get']('db:user') }}
+ - host: '%'
+
+Start and enable the web server:
+ service.running:
+ - name: apache2
+ - enable: True
+ - watch:
+ - pkg: Install LAMP stack
+
+Configure Apache user:
+ user.present:
+ - name: {{ salt['pillar.get']('fs:user') }}
+ - password: {{ salt['pillar.get']('fs:password') }}
+ - hash_password: True
+ - shell: /bin/bash
+
+Ensure docroot has the correct permissions:
+ file.directory:
+ - name: {{ salt['pillar.get']('fs:path') }}
+ - user: {{ salt['pillar.get']('fs:user') }}
+ - group: {{ salt['pillar.get']('fs:user') }}
+ - recurse:
+ - user
+ - group
+ - makedirs: True
diff --git a/e107_tests/lib/ci/salt/states/files/etc/mysql/mariadb.conf.d/99-overrides.cnf b/e107_tests/lib/ci/salt/states/files/etc/mysql/mariadb.conf.d/99-overrides.cnf
new file mode 100644
index 000000000..f759a4963
--- /dev/null
+++ b/e107_tests/lib/ci/salt/states/files/etc/mysql/mariadb.conf.d/99-overrides.cnf
@@ -0,0 +1,2 @@
+[mysqld]
+bind-address = 0.0.0.0
diff --git a/e107_tests/lib/config.php b/e107_tests/lib/config.php
new file mode 100644
index 000000000..3933179e3
--- /dev/null
+++ b/e107_tests/lib/config.php
@@ -0,0 +1,18 @@
+params = $params;
+ }
+
+ protected static function println($text = '')
+ {
+ codecept_debug($text);
+
+ //echo("${text}\n");
+
+ //$prefix = debug_backtrace()[1]['function'];
+ //echo("[\033[1m${prefix}\033[0m] ${text}\n");
+ }
+
+ protected $components = array();
+
+ /**
+ * @param array $components
+ */
+ public function setComponents($components)
+ {
+ $this->components = $components;
+ }
+
+ public function unlinkAppFile($relative_path)
+ {
+ throw new \PHPUnit\Framework\SkippedTestError("Test wants \"$relative_path\" to be deleted from the app, ".
+ "but the configured deployer ".get_class($this)." is not capable of doing that.");
+ }
+
+ /**
+ * Methods not implemented
+ *
+ * @param $method_name
+ * @param $arguments
+ * @return null
+ */
+ public function __call($method_name, $arguments)
+ {
+ throw new BadMethodCallException(get_class($this)."::$method_name is not implemented");
+ }
+}
\ No newline at end of file
diff --git a/e107_tests/lib/deployers/DeployerFactory.php b/e107_tests/lib/deployers/DeployerFactory.php
new file mode 100644
index 000000000..0e8afa157
--- /dev/null
+++ b/e107_tests/lib/deployers/DeployerFactory.php
@@ -0,0 +1,42 @@
+components))
+ {
+ $this->start_fs();
+ }
+ }
+
+ private function getFsParams()
+ {
+ return $this->params['fs'];
+ }
+
+ private function generateSshpassPrefix()
+ {
+ if (empty($this->getFsParam('privkey_path')) &&
+ !empty($this->getFsParam('password')))
+ {
+ return 'sshpass -p '.escapeshellarg($this->getFsParam('password')).' ';
+ }
+ return '';
+ }
+
+ private function getFsParam($key)
+ {
+ return $this->getFsParams()[$key];
+ }
+
+ private function generateRsyncRemoteShell()
+ {
+ $prefix = 'ssh -p '.escapeshellarg($this->getFsParam('port'));
+ if (!empty($this->getFsParam('privkey_path')))
+ return $prefix.' -i ' . escapeshellarg($this->getFsParam('privkey_path'));
+ else
+ return $prefix;
+ }
+
+ private static function runCommand($command, &$stdout = null, &$stderr = null)
+ {
+ $descriptorSpec = [
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ];
+ $pipes = [];
+ self::println("Running this command…:");
+ self::println($command);
+ $resource = proc_open($command, $descriptorSpec, $pipes, APP_PATH);
+ $stdout = stream_get_contents($pipes[1]);
+ $stderr = stream_get_contents($pipes[2]);
+ self::println("---------- stdout ----------");
+ self::println(trim($stdout));
+ self::println("---------- stderr ----------");
+ self::println(trim($stderr));
+ self::println("----------------------------");
+ foreach ($pipes as $pipe)
+ {
+ fclose($pipe);
+ }
+ return proc_close($resource);
+ }
+
+ public function stop()
+ {
+ self::println("=== SFTP Deployer – Tear Down ===");
+ }
+
+ public function unlinkAppFile($relative_path)
+ {
+ self::println("Deleting file \"$relative_path\" from deployed test location…");
+ $fs_params = $this->getFsParams();
+ $command = $this->generateSshpassPrefix().
+ $this->generateRsyncRemoteShell().
+ " ".escapeshellarg("{$fs_params['user']}@{$fs_params['host']}").
+ " ".escapeshellarg("rm -v " . escapeshellarg(rtrim($fs_params['path'], '/')."/$relative_path"));
+ $retcode = self::runCommand($command);
+ if ($retcode === 0)
+ {
+ self::println("Deleted file \"$relative_path\" from deployed test location");
+ }
+ else
+ {
+ self::println("No such file to delete: \"$relative_path\"");
+ }
+ }
+
+ private function start_fs()
+ {
+ $fs_params = $this->getFsParams();
+ $fs_params['path'] = rtrim($fs_params['path'], '/') . '/';
+ $command = $this->generateSshpassPrefix() .
+ 'rsync -e ' .
+ escapeshellarg($this->generateRsyncRemoteShell()) .
+ ' --delete -avzHXShs ' .
+ escapeshellarg(rtrim(APP_PATH, '/') . '/') . ' ' .
+ escapeshellarg("{$fs_params['user']}@{$fs_params['host']}:{$fs_params['path']}");
+ $retcode = self::runCommand($command);
+ if ($retcode !== 0) {
+ throw new Exception("SFTP deployment failed. Run with --debug to see stdout and stderr.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/e107_tests/lib/deployers/cPanelDeployer.php b/e107_tests/lib/deployers/cPanelDeployer.php
new file mode 100644
index 000000000..94b58dee2
--- /dev/null
+++ b/e107_tests/lib/deployers/cPanelDeployer.php
@@ -0,0 +1,374 @@
+credentials = $params['hosting'];
+ }
+
+ public function start()
+ {
+ self::println();
+ self::println("=== cPanel Deployer – Bring Up ===");
+ $creds = $this->credentials;
+ if (!$creds['hostname'] ||
+ !$creds['username'] ||
+ !$creds['password'])
+ {
+ throw new Exception("Cannot deploy cPanel environment because credentials are missing.");
+ }
+
+ $this->prepare();
+
+ foreach ($this->components as $component)
+ {
+ $method = "prepare_{$component}";
+ if (!method_exists($this, $method))
+ {
+ throw new Exception("Unsupported component \"{$component}\" requested.");
+ }
+ }
+ foreach ($this->components as $component)
+ {
+ $method = "prepare_{$component}";
+ $this->$method();
+ }
+ }
+
+ private function prepare()
+ {
+ $username = &$this->credentials['username'];
+ $password = &$this->credentials['password'];
+ $hostname = &$this->credentials['hostname'];
+
+ $this->run_id = $run_id = uniqid(self::TEST_PREFIX);
+
+ self::println("Test run ID: ".$this->run_id);
+
+ $this->cPanel = $cPanel = new cpanelAPI($username, $password, $hostname);
+
+ self::println("Connecting to cPanel at \"{$hostname}\" with username \"{$username}\"…");
+
+ $domains_data = $cPanel->uapi->DomainInfo->domains_data();
+ if (!$domains_data)
+ {
+ throw new Exception("Cannot connect to cPanel at \"{$hostname}\" with username \"{$username}\" and password \"{$password}\"");
+ }
+ $userdata = $domains_data->{'data'};
+ $this->homedir = $userdata->{'main_domain'}->{'homedir'};
+ $this->docroot = $userdata->{'main_domain'}->{'documentroot'};
+ $this->domain = $userdata->{'main_domain'}->{'domain'};
+
+ self::println("Obtained home directory from cPanel: " . $this->homedir);
+ self::println("Obtained document root from cPanel: " . $this->docroot);
+ self::println("Obtained domain name from cPanel: " . $this->domain);
+
+ $acceptance_tests = self::get_active_acceptance_tests($cPanel, $this->homedir);
+
+ self::println("Adding this test (".$this->run_id.") to registered tests list…");
+ $run_time = microtime(true);
+ array_push($acceptance_tests,
+ ['id' => $run_id,
+ 'time' => $run_time
+ ]);
+
+ self::write_acceptance_tests($cPanel, $this->homedir, $acceptance_tests);
+
+ $valid_acceptance_test_ids = self::get_acceptance_test_ids($acceptance_tests);
+ self::println("Current unexpired tests: [".implode(", ", $valid_acceptance_test_ids)."]");
+
+ self::prune_inactive_acceptance_test_resources($cPanel, $valid_acceptance_test_ids);
+ }
+
+ private static function get_active_acceptance_tests($cPanel, $homedir)
+ {
+ self::println("Retrieving existing registered tests from cPanel account…");
+ $acceptance_tests = [];
+ $acceptance_tests_apiresponse = $cPanel->uapi->Fileman->get_file_content(['dir' => $homedir, 'file' => 'acceptance_tests.status.txt']);
+ if (!is_null($acceptance_tests_apiresponse->{'data'}))
+ {
+ $acceptance_tests_raw = $acceptance_tests_apiresponse->{'data'}->{'content'};
+ $acceptance_tests = (array) json_decode($acceptance_tests_raw, true);
+ self::prune_acceptance_tests($acceptance_tests);
+ }
+ return $acceptance_tests;
+ }
+
+ private static function prune_acceptance_tests(array &$list, $id_to_remove = null)
+ {
+ foreach ($list as $key => $item)
+ {
+ $time = $item['time'];
+ if ($item['id'] === $id_to_remove || $time <= strtotime("now - 10 seconds"))
+ {
+ unset($list[$key]);
+ }
+ }
+ $list = array_values($list);
+ return $list;
+ }
+
+ private static function write_acceptance_tests($cPanel, $homedir, $acceptance_tests)
+ {
+ $acceptance_tests_json = json_encode($acceptance_tests, JSON_PRETTY_PRINT);
+
+ self::println("Saving registered tests list to cPanel account…");
+ $cPanel->uapi->Fileman->save_file_content(['dir' => $homedir, 'file' => 'acceptance_tests.status.txt', 'content' => $acceptance_tests_json]);
+ }
+
+ private static function get_acceptance_test_ids(array $list)
+ {
+ $ids = [];
+ foreach ($list as $item)
+ {
+ $ids[] = $item['id'];
+ }
+ return $ids;
+ }
+
+ private static function prune_inactive_acceptance_test_resources($cPanel, $valid_acceptance_test_ids)
+ {
+ self::println("Pruning expired tests…");
+ $listdbs = $cPanel->api2->MysqlFE->listdbs()->{'cpanelresult'}->{'data'};
+ self::prune_mysql_databases($listdbs, $valid_acceptance_test_ids, $cPanel);
+
+ $listdbusers = $cPanel->api2->MysqlFE->listusers()->{'cpanelresult'}->{'data'};
+ self::prune_mysql_users($listdbusers, $valid_acceptance_test_ids, $cPanel);
+
+ $target_files_apiresponse = $cPanel->uapi->Fileman->list_files(['dir' => self::TARGET_RELPATH]);
+ $target_files = $target_files_apiresponse->{'data'};
+ foreach ($target_files as $target_file)
+ {
+ $questionable_filename = $target_file->{'file'};
+ if (substr($questionable_filename, 0, strlen(self::TEST_PREFIX)) === self::TEST_PREFIX &&
+ !in_array($questionable_filename, $valid_acceptance_test_ids))
+ {
+ self::println("Deleting expired test folder \"".self::TARGET_RELPATH.$questionable_filename."\"…");
+ $cPanel->api2->Fileman->fileop(['op' => 'unlink', 'sourcefiles' => self::TARGET_RELPATH.$questionable_filename]);
+ }
+ }
+ }
+
+ private static function prune_mysql_databases($dbs, $ids, $cPanel)
+ {
+ $prefix = $cPanel->user."_".self::TEST_PREFIX;
+ foreach ($dbs as $db)
+ {
+ $db = (array) $db;
+ if (substr($db['db'], 0, strlen($prefix)) !== $prefix)
+ continue;
+ $questionable_db = substr($db['db'], strlen($prefix));
+ if (!in_array($questionable_db, $ids))
+ {
+ self::println("Deleting expired MySQL database \"".$db['db']."\"…");
+ $cPanel->uapi->Mysql->delete_database(['name' => $db['db']]);
+ }
+ }
+ }
+
+ private static function prune_mysql_users($users, $ids, $cPanel)
+ {
+ $prefix = $cPanel->user."_".self::TEST_PREFIX;
+ foreach ($users as $user)
+ {
+ $user = (array) $user;
+ if (substr($user['user'], 0, strlen($prefix)) !== $prefix)
+ continue;
+ $questionable_user = substr($user['user'], strlen($prefix));
+ if (!in_array($questionable_user, $ids))
+ {
+ self::println("Deleting expired MySQL user \"".$user['user']."\"…");
+ $cPanel->uapi->Mysql->delete_user(['name' => $user['user']]);
+ }
+ }
+ }
+
+ public function stop()
+ {
+ self::println("=== cPanel Deployer – Tear Down ===");
+ $cPanel = $this->cPanel;
+ $acceptance_tests = self::get_active_acceptance_tests($cPanel, $this->homedir);
+ self::println("Removing this test (".$this->run_id.") from registered tests list…");
+ self::prune_acceptance_tests($acceptance_tests, $this->run_id);
+ self::write_acceptance_tests($cPanel, $this->homedir, $acceptance_tests);
+
+ $valid_acceptance_test_ids = self::get_acceptance_test_ids($acceptance_tests);
+ self::println("Current unexpired tests: [".implode(", ", $valid_acceptance_test_ids)."]");
+ self::prune_inactive_acceptance_test_resources($cPanel, $valid_acceptance_test_ids);
+
+ if (!$this->skip_mysql_remote_hosts)
+ {
+ self::clean_mysql_remote_hosts($cPanel);
+ }
+ }
+
+ private static function clean_mysql_remote_hosts($cPanel)
+ {
+ $remote_hosts = $cPanel->api2->MysqlFE->gethosts()->{'cpanelresult'}->{'data'};
+ if (in_array('%', $remote_hosts, true))
+ {
+ self::println("Removing cPanel MySQL remote host '%'…");
+ $cPanel->uapi->Mysql->delete_host(['host' => '%']);
+ }
+ }
+
+ public function reconfigure_db($module)
+ {
+ $db = $module->getDbModule();
+ $Db_config = $db->_getConfig();
+ $Db_config['dsn'] = $this->getDsn();
+ $Db_config['user'] = $this->getDbUsername();
+ $Db_config['password'] = $this->getDbPassword();
+ $db->_reconfigure($Db_config);
+ // Next line is used to make connection available to any code after this point
+ //$this->getModule('\Helper\DelayedDb')->_delayedInitialize();
+ }
+
+ private function getDsn()
+ {
+ $hostname = $this->credentials['hostname'];
+ $db_id = $this->getDbName();
+ return "mysql:host={$hostname};dbname={$db_id}";
+ }
+
+ private function getDbName()
+ {
+ return $this->db_id;
+ }
+
+ private function getDbUsername()
+ {
+ return $this->db_id;
+ }
+
+ private function getDbPassword()
+ {
+ return $this->run_id;
+ }
+
+ public function reconfigure_fs($module)
+ {
+ $url = $this->getUrl();
+ $browser = $module->getBrowserModule();
+ $browser->_reconfigure(array('url' => $url));
+ }
+
+ private function getUrl()
+ {
+ return "http://".$this->domain."/".$this->run_id."/";
+ }
+
+ public function unlinkAppFile($relative_path)
+ {
+ self::println("Deleting file \"$relative_path\" from deployed test location…");
+ $this->cPanel->api2->Fileman->fileop(['op' => 'unlink',
+ 'sourcefiles' => self::TARGET_RELPATH.$this->run_id."/".$relative_path]);
+ }
+
+ private function prepare_db()
+ {
+ $cPanel = $this->cPanel;
+ $username = &$this->credentials['username'];
+ $run_id = &$this->run_id;
+ $this->db_id = $db_id = "{$username}_{$run_id}";
+
+ self::println("Ensuring that MySQL users allow any remote access hosts (%)…");
+ $remote_hosts = $cPanel->api2->MysqlFE->gethosts()->{'cpanelresult'}->{'data'};
+ if (!in_array('%', $remote_hosts, true))
+ {
+ $cPanel->uapi->Mysql->add_host(['host' => '%']);
+ register_shutdown_function(function() use ($cPanel)
+ {
+ self::clean_mysql_remote_hosts($cPanel);
+ });
+ }
+ else
+ {
+ $this->skip_mysql_remote_hosts = true;
+ }
+
+ self::println("Creating new MySQL database \"{$db_id}\"…");
+ $cPanel->uapi->Mysql->create_database(['name' => $db_id]);
+
+ self::println("Creating new MySQL user \"{$db_id}\" with password \"{$run_id}\"…");
+ $cPanel->uapi->Mysql->create_user(['name' => $db_id, 'password' => $run_id]);
+ self::println("Granting ALL PRIVILEGES to MySQL user \"{$db_id}\"…");
+ $cPanel->uapi->Mysql->set_privileges_on_database(['user' => $db_id,
+ 'database' => $db_id,
+ 'privileges' => 'ALL PRIVILEGES'
+ ]);
+ }
+
+ private function prepare_fs()
+ {
+ $cPanel = $this->cPanel;
+ $app_archive = self::archive_app(APP_PATH, $this->run_id);
+ $app_archive_path = stream_get_meta_data($app_archive)['uri'];
+ $app_archive_name = basename($app_archive_path);
+ self::println("Sending archive to cPanel server…");
+ $cPanel->uapi->post->Fileman
+ ->upload_files(['dir' => self::TARGET_RELPATH,
+ 'file-1' => new CURLFile($app_archive_path)
+ ]);
+ self::println("Extracting archive on cPanel server…");
+ $cPanel->api2->Fileman
+ ->fileop(['op' => 'extract',
+ 'sourcefiles' => self::TARGET_RELPATH.$app_archive_name,
+ 'destfiles' => '.'
+ ]);
+ self::println("Deleting archive from cPanel server…");
+ $cPanel->api2->Fileman
+ ->fileop(['op' => 'unlink',
+ 'sourcefiles' => self::TARGET_RELPATH.$app_archive_name
+ ]);
+ }
+
+ private static function archive_app($path, $prefix = '')
+ {
+ $tmp_file = tmpfile();
+ $tmp_file_path = stream_get_meta_data($tmp_file)['uri'];
+ self::println("Touched temporary archive file; path: ".$tmp_file_path);
+ $archive = new ZipArchive();
+ $archive->open($tmp_file_path, ZipArchive::OVERWRITE);
+ $i = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+ self::println("Adding app to temporary archive…");
+ $path = realpath($path);
+ /**
+ * @var $file_info SplFileInfo
+ */
+ foreach ($i as $file_info)
+ {
+ $realpath = $file_info->getRealPath();
+ if (substr($realpath, 0, strlen($path)) === $path)
+ $relpath = substr($realpath, strlen($path));
+ if (substr($relpath, -3) === "/.." ||
+ substr($relpath, -2) === "/." ||
+ !file_exists($realpath) ||
+ !is_file($realpath) ||
+ empty($relpath)) continue;
+ $relpath = $prefix . $relpath;
+ $archive->addFile($realpath, $relpath);
+ $archive->setExternalAttributesName($relpath, ZipArchive::OPSYS_UNIX, fileperms($realpath) << 16);
+ }
+ $archive->close();
+
+ return $tmp_file;
+ }
+}
diff --git a/e107_tests/lib/preparers/E107Preparer.php b/e107_tests/lib/preparers/E107Preparer.php
new file mode 100644
index 000000000..b736e6a10
--- /dev/null
+++ b/e107_tests/lib/preparers/E107Preparer.php
@@ -0,0 +1,66 @@
+deleteHashDirs();
+ }
+
+ public function rollback()
+ {
+ return $this->deleteHashDirs();
+ }
+
+ protected function deleteHashDirs()
+ {
+ $system = APP_PATH."/e107_system/".self::TEST_HASH;
+ $this->deleteDir($system);
+
+ $media = APP_PATH."/e107_media/".self::TEST_HASH;
+ $this->deleteDir($media);
+
+ if(is_dir($system))
+ {
+ throw new Exception(get_class() . " couldn't delete ".$system);
+ }
+
+ }
+
+ private function deleteDir($dirPath)
+ {
+ codecept_debug(get_class() . ' is deleting '.escapeshellarg($dirPath).'…');
+
+ if(!is_dir($dirPath))
+ {
+ // echo ($dirPath . "must be a directory");
+ return null;
+ }
+
+ if(substr($dirPath, strlen($dirPath) - 1, 1) != '/')
+ {
+ $dirPath .= '/';
+ }
+
+ $files = glob($dirPath . '*', GLOB_MARK);
+
+ foreach($files as $file)
+ {
+ if(is_dir($file))
+ {
+ $this->deleteDir($file);
+ }
+ else
+ {
+ unlink($file);
+ }
+ }
+
+ if(is_dir($dirPath))
+ {
+ rmdir($dirPath);
+ }
+ }
+}
diff --git a/e107_tests/lib/preparers/GitPreparer.php b/e107_tests/lib/preparers/GitPreparer.php
new file mode 100644
index 000000000..6c7b761fa
--- /dev/null
+++ b/e107_tests/lib/preparers/GitPreparer.php
@@ -0,0 +1,139 @@
+debug('Snapshot requested');
+ return $this->setVcsInProgress();
+ }
+
+ public function rollback()
+ {
+ $this->debug('Rollback requested');
+ return $this->unsetVcsInProgress();
+ }
+
+ protected function setVcsInProgress()
+ {
+ // Cleanup in case of a fatal error
+ PriorityCallbacks::instance()->register_shutdown_function([$this, 'rollback']);
+
+ if ($this->isVcsInProgress())
+ {
+ $this->debug('Git repo shows test in progress. Probably crashed test.');
+ $this->unsetVcsInProgress();
+ }
+
+ $this->debug('Setting test locks in Git…');
+
+ touch(self::TEST_IN_PROGRESS_FILE);
+ $this->runCommand('git add -f '.escapeshellarg(self::TEST_IN_PROGRESS_FILE));
+ $this->runCommand('git add -A -f');
+
+ $commit_command = 'git -c user.name="Test Run" -c user.email="testrun@example.com" commit -a --no-gpg-sign ' .
+ "-m '".self::TEST_IN_PROGRESS."! If test crashed, run `git log -1` for instructions' " .
+ "-m 'Running the test again after fixing the crash will clear this commit\nand any related stashes.' " .
+ "-m 'Alternatively, run these commands to restore the repository to its\npre-test state:' ";
+ $unsetVcsInProgress_commands = [
+ 'git reset --hard HEAD',
+ 'git clean -fdx',
+ 'git reset --mixed HEAD^',
+ 'rm -fv '.escapeshellarg(self::TEST_IN_PROGRESS)
+ ];
+ foreach($unsetVcsInProgress_commands as $command)
+ {
+ $commit_command .= "-m ".escapeshellarg($command)." ";
+ }
+
+ $stdout = '';
+ $stderr = '';
+ $rc = $this->runCommand($commit_command, $stdout, $stderr);
+ if ($rc !== 0)
+ {
+ @unlink(self::TEST_IN_PROGRESS_FILE);
+ $this->debug('Error taking snapshot with Git!');
+ $this->debug('========== STDOUT ==========');
+ $this->debug($stdout);
+ $this->debug('========== STDERR ==========');
+ $this->debug($stderr);
+ throw new Exception("Error taking snapshot with Git!");
+ }
+ }
+
+ protected function isVcsInProgress($case = '')
+ {
+ $in_progress = [];
+
+ $in_progress['file'] = file_exists(self::TEST_IN_PROGRESS_FILE);
+
+ $stdout = '';
+ $this->runCommand('git log -1 --pretty=%B', $stdout);
+ $in_progress['commit'] = strpos($stdout, self::TEST_IN_PROGRESS) !== false;
+
+ $stdout = '';
+ $this->runCommand('git stash list', $stdout);
+ $in_progress['stash'] = strpos($stdout, self::TEST_IN_PROGRESS) !== false;
+
+ if(!empty($case)) return $in_progress[$case];
+ return in_array(true, $in_progress);
+ }
+
+ /**
+ * @param string $command The command to run
+ * @param string $stdout Reference to the STDOUT output as a string
+ * @param string $stderr Reference to the STDERR output as a string
+ * @return int Return code of the command that was run
+ */
+ protected function runCommand($command, &$stdout = "", &$stderr = "")
+ {
+ $descriptorspec = [
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ];
+ $pipes = [];
+ $resource = proc_open($command, $descriptorspec, $pipes, APP_PATH);
+ $stdout .= stream_get_contents($pipes[1]);
+ $stderr .= stream_get_contents($pipes[2]);
+ foreach ($pipes as $pipe)
+ {
+ fclose($pipe);
+ }
+ return proc_close($resource);
+ }
+
+ protected function unsetVcsInProgress()
+ {
+ if (!$this->isVcsInProgress())
+ {
+ $this->debug('No test locks found');
+ return;
+ }
+
+ $this->debug('Rolling back Git repo to pre-test state…');
+ $this->runCommand('git reset --hard HEAD');
+ $this->runCommand('git clean -fdx');
+
+ while ($this->isVcsInProgress('commit'))
+ {
+ $this->debug('Going back one commit…');
+ $this->runCommand('git reset --mixed HEAD^');
+ }
+
+ while ($this->isVcsInProgress('stash'))
+ {
+ $this->debug('Popping top of stash…');
+ $this->runCommand('git stash pop');
+ }
+
+ @unlink(self::TEST_IN_PROGRESS_FILE);
+ }
+
+ protected function debug($message)
+ {
+ codecept_debug(get_class() . ': ' . $message);
+ }
+}
diff --git a/e107_tests/lib/preparers/Preparer.php b/e107_tests/lib/preparers/Preparer.php
new file mode 100644
index 000000000..81f7f8ffb
--- /dev/null
+++ b/e107_tests/lib/preparers/Preparer.php
@@ -0,0 +1,7 @@
+
+
+
+
+
+ tests
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e107_tests/tests/_bootstrap.php b/e107_tests/tests/_bootstrap.php
new file mode 100644
index 000000000..f2f572678
--- /dev/null
+++ b/e107_tests/tests/_bootstrap.php
@@ -0,0 +1,18 @@
+mcpeace.");
+define("MAW_THEME_2", "maw");
+
+?>
\ No newline at end of file
diff --git a/e107_tests/tests/_data/basic-light/languages/index.html b/e107_tests/tests/_data/basic-light/languages/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.eot b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 000000000..b93a4953f
Binary files /dev/null and b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.eot differ
diff --git a/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.svg b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 000000000..94fb5490a
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+
+
+
\ No newline at end of file
diff --git a/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.ttf b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 000000000..1413fc609
Binary files /dev/null and b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 000000000..9e612858f
Binary files /dev/null and b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff differ
diff --git a/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff2 b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 000000000..64539b54c
Binary files /dev/null and b/e107_tests/tests/_data/basic-light/maw/css/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/e107_tests/tests/_data/basic-light/maw/css/ie10-viewport-bug-workaround.css b/e107_tests/tests/_data/basic-light/maw/css/ie10-viewport-bug-workaround.css
new file mode 100644
index 000000000..8b3803b48
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/maw/css/ie10-viewport-bug-workaround.css
@@ -0,0 +1,15 @@
+/*!
+ * IE10 viewport hack for Surface/desktop Windows 8 bug
+ * Copyright 2014-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+/*
+ * See the Getting Started docs for more information:
+ * http://getbootstrap.com/getting-started/#support-ie10-width
+ */
+@-webkit-viewport { width: device-width; }
+@-moz-viewport { width: device-width; }
+@-ms-viewport { width: device-width; }
+@-o-viewport { width: device-width; }
+@viewport { width: device-width; }
diff --git a/e107_tests/tests/_data/basic-light/maw/css/index.html b/e107_tests/tests/_data/basic-light/maw/css/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/tests/_data/basic-light/maw/css/theme.min.css b/e107_tests/tests/_data/basic-light/maw/css/theme.min.css
new file mode 100644
index 000000000..9f0202b37
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/maw/css/theme.min.css
@@ -0,0 +1,1466 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto:400,700);
+body,figure{margin:0}
+.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.pre-scrollable{max-height:340px}
+html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-size:10px;-webkit-tap-highlight-color:transparent}
+article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}
+audio,canvas,progress,video{display:inline-block;vertical-align:baseline}
+audio:not([controls]){display:none;height:0}
+[hidden],template{display:none}
+a{background-color:transparent}
+a:active,a:hover{outline:0}
+b,optgroup,strong{font-weight:700}
+dfn{font-style:italic}
+h1{margin:.67em 0}
+mark{background:#ff0;color:#000}
+sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
+sup{top:-.5em}
+sub{bottom:-.25em}
+img{border:0;vertical-align:middle}
+svg:not(:root){overflow:hidden}
+hr{box-sizing:content-box;height:0}
+pre,textarea{overflow:auto}
+code,kbd,pre,samp{font-size:1em}
+button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}
+button{overflow:visible}
+button,select{text-transform:none}
+button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
+button[disabled],html input[disabled]{cursor:default}
+button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
+input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}
+input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}
+input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}
+table{border-collapse:collapse;border-spacing:0}
+td,th{padding:0}
+@media print{blockquote,img,pre,tr{page-break-inside:avoid}
+*,:after,:before{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}
+a,a:visited{text-decoration:none}
+a[href]:after{content:" (" attr(href) ")"}
+abbr[title]:after{content:" (" attr(title) ")"}
+a[href^="javascript:"]:after,a[href^="#"]:after{content:""}
+blockquote,pre{border:1px solid #999}
+thead{display:table-header-group}
+img{max-width:100%!important}
+h2,h3,p{orphans:3;widows:3}
+h2,h3{page-break-after:avoid}
+.navbar{display:none}
+.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}
+.label{border:1px solid #000}
+.table{border-collapse:collapse!important}
+.table td,.table th{background-color:#fff!important;color:#333}
+.table-bordered td,.table-bordered th{border:1px solid #ddd!important}
+}
+.img-thumbnail,body{background-color:#f1f1f1}
+.btn,.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active,.btn.active,.btn:active,.form-control,.navbar-toggle,.open>.dropdown-toggle.btn-danger,.open>.dropdown-toggle.btn-default,.open>.dropdown-toggle.btn-info,.open>.dropdown-toggle.btn-primary,.open>.dropdown-toggle.btn-success,.open>.dropdown-toggle.btn-warning{background-image:none}
+@font-face{font-family:'Glyphicons Halflings';src:url(fonts/glyphicons-halflings-regular.eot);src:url(fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(fonts/glyphicons-halflings-regular.woff) format('woff'),url(fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}
+.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
+.glyphicon-asterisk:before{content:"\2a"}
+.glyphicon-plus:before{content:"\2b"}
+.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}
+.glyphicon-minus:before{content:"\2212"}
+.glyphicon-cloud:before{content:"\2601"}
+.glyphicon-envelope:before{content:"\2709"}
+.glyphicon-pencil:before{content:"\270f"}
+.glyphicon-glass:before{content:"\e001"}
+.glyphicon-music:before{content:"\e002"}
+.glyphicon-search:before{content:"\e003"}
+.glyphicon-heart:before{content:"\e005"}
+.glyphicon-star:before{content:"\e006"}
+.glyphicon-star-empty:before{content:"\e007"}
+.glyphicon-user:before{content:"\e008"}
+.glyphicon-film:before{content:"\e009"}
+.glyphicon-th-large:before{content:"\e010"}
+.glyphicon-th:before{content:"\e011"}
+.glyphicon-th-list:before{content:"\e012"}
+.glyphicon-ok:before{content:"\e013"}
+.glyphicon-remove:before{content:"\e014"}
+.glyphicon-zoom-in:before{content:"\e015"}
+.glyphicon-zoom-out:before{content:"\e016"}
+.glyphicon-off:before{content:"\e017"}
+.glyphicon-signal:before{content:"\e018"}
+.glyphicon-cog:before{content:"\e019"}
+.glyphicon-trash:before{content:"\e020"}
+.glyphicon-home:before{content:"\e021"}
+.glyphicon-file:before{content:"\e022"}
+.glyphicon-time:before{content:"\e023"}
+.glyphicon-road:before{content:"\e024"}
+.glyphicon-download-alt:before{content:"\e025"}
+.glyphicon-download:before{content:"\e026"}
+.glyphicon-upload:before{content:"\e027"}
+.glyphicon-inbox:before{content:"\e028"}
+.glyphicon-play-circle:before{content:"\e029"}
+.glyphicon-repeat:before{content:"\e030"}
+.glyphicon-refresh:before{content:"\e031"}
+.glyphicon-list-alt:before{content:"\e032"}
+.glyphicon-lock:before{content:"\e033"}
+.glyphicon-flag:before{content:"\e034"}
+.glyphicon-headphones:before{content:"\e035"}
+.glyphicon-volume-off:before{content:"\e036"}
+.glyphicon-volume-down:before{content:"\e037"}
+.glyphicon-volume-up:before{content:"\e038"}
+.glyphicon-qrcode:before{content:"\e039"}
+.glyphicon-barcode:before{content:"\e040"}
+.glyphicon-tag:before{content:"\e041"}
+.glyphicon-tags:before{content:"\e042"}
+.glyphicon-book:before{content:"\e043"}
+.glyphicon-bookmark:before{content:"\e044"}
+.glyphicon-print:before{content:"\e045"}
+.glyphicon-camera:before{content:"\e046"}
+.glyphicon-font:before{content:"\e047"}
+.glyphicon-bold:before{content:"\e048"}
+.glyphicon-italic:before{content:"\e049"}
+.glyphicon-text-height:before{content:"\e050"}
+.glyphicon-text-width:before{content:"\e051"}
+.glyphicon-align-left:before{content:"\e052"}
+.glyphicon-align-center:before{content:"\e053"}
+.glyphicon-align-right:before{content:"\e054"}
+.glyphicon-align-justify:before{content:"\e055"}
+.glyphicon-list:before{content:"\e056"}
+.glyphicon-indent-left:before{content:"\e057"}
+.glyphicon-indent-right:before{content:"\e058"}
+.glyphicon-facetime-video:before{content:"\e059"}
+.glyphicon-picture:before{content:"\e060"}
+.glyphicon-map-marker:before{content:"\e062"}
+.glyphicon-adjust:before{content:"\e063"}
+.glyphicon-tint:before{content:"\e064"}
+.glyphicon-edit:before{content:"\e065"}
+.glyphicon-share:before{content:"\e066"}
+.glyphicon-check:before{content:"\e067"}
+.glyphicon-move:before{content:"\e068"}
+.glyphicon-step-backward:before{content:"\e069"}
+.glyphicon-fast-backward:before{content:"\e070"}
+.glyphicon-backward:before{content:"\e071"}
+.glyphicon-play:before{content:"\e072"}
+.glyphicon-pause:before{content:"\e073"}
+.glyphicon-stop:before{content:"\e074"}
+.glyphicon-forward:before{content:"\e075"}
+.glyphicon-fast-forward:before{content:"\e076"}
+.glyphicon-step-forward:before{content:"\e077"}
+.glyphicon-eject:before{content:"\e078"}
+.glyphicon-chevron-left:before{content:"\e079"}
+.glyphicon-chevron-right:before{content:"\e080"}
+.glyphicon-plus-sign:before{content:"\e081"}
+.glyphicon-minus-sign:before{content:"\e082"}
+.glyphicon-remove-sign:before{content:"\e083"}
+.glyphicon-ok-sign:before{content:"\e084"}
+.glyphicon-question-sign:before{content:"\e085"}
+.glyphicon-info-sign:before{content:"\e086"}
+.glyphicon-screenshot:before{content:"\e087"}
+.glyphicon-remove-circle:before{content:"\e088"}
+.glyphicon-ok-circle:before{content:"\e089"}
+.glyphicon-ban-circle:before{content:"\e090"}
+.glyphicon-arrow-left:before{content:"\e091"}
+.glyphicon-arrow-right:before{content:"\e092"}
+.glyphicon-arrow-up:before{content:"\e093"}
+.glyphicon-arrow-down:before{content:"\e094"}
+.glyphicon-share-alt:before{content:"\e095"}
+.glyphicon-resize-full:before{content:"\e096"}
+.glyphicon-resize-small:before{content:"\e097"}
+.glyphicon-exclamation-sign:before{content:"\e101"}
+.glyphicon-gift:before{content:"\e102"}
+.glyphicon-leaf:before{content:"\e103"}
+.glyphicon-fire:before{content:"\e104"}
+.glyphicon-eye-open:before{content:"\e105"}
+.glyphicon-eye-close:before{content:"\e106"}
+.glyphicon-warning-sign:before{content:"\e107"}
+.glyphicon-plane:before{content:"\e108"}
+.glyphicon-calendar:before{content:"\e109"}
+.glyphicon-random:before{content:"\e110"}
+.glyphicon-comment:before{content:"\e111"}
+.glyphicon-magnet:before{content:"\e112"}
+.glyphicon-chevron-up:before{content:"\e113"}
+.glyphicon-chevron-down:before{content:"\e114"}
+.glyphicon-retweet:before{content:"\e115"}
+.glyphicon-shopping-cart:before{content:"\e116"}
+.glyphicon-folder-close:before{content:"\e117"}
+.glyphicon-folder-open:before{content:"\e118"}
+.glyphicon-resize-vertical:before{content:"\e119"}
+.glyphicon-resize-horizontal:before{content:"\e120"}
+.glyphicon-hdd:before{content:"\e121"}
+.glyphicon-bullhorn:before{content:"\e122"}
+.glyphicon-bell:before{content:"\e123"}
+.glyphicon-certificate:before{content:"\e124"}
+.glyphicon-thumbs-up:before{content:"\e125"}
+.glyphicon-thumbs-down:before{content:"\e126"}
+.glyphicon-hand-right:before{content:"\e127"}
+.glyphicon-hand-left:before{content:"\e128"}
+.glyphicon-hand-up:before{content:"\e129"}
+.glyphicon-hand-down:before{content:"\e130"}
+.glyphicon-circle-arrow-right:before{content:"\e131"}
+.glyphicon-circle-arrow-left:before{content:"\e132"}
+.glyphicon-circle-arrow-up:before{content:"\e133"}
+.glyphicon-circle-arrow-down:before{content:"\e134"}
+.glyphicon-globe:before{content:"\e135"}
+.glyphicon-wrench:before{content:"\e136"}
+.glyphicon-tasks:before{content:"\e137"}
+.glyphicon-filter:before{content:"\e138"}
+.glyphicon-briefcase:before{content:"\e139"}
+.glyphicon-fullscreen:before{content:"\e140"}
+.glyphicon-dashboard:before{content:"\e141"}
+.glyphicon-paperclip:before{content:"\e142"}
+.glyphicon-heart-empty:before{content:"\e143"}
+.glyphicon-link:before{content:"\e144"}
+.glyphicon-phone:before{content:"\e145"}
+.glyphicon-pushpin:before{content:"\e146"}
+.glyphicon-usd:before{content:"\e148"}
+.glyphicon-gbp:before{content:"\e149"}
+.glyphicon-sort:before{content:"\e150"}
+.glyphicon-sort-by-alphabet:before{content:"\e151"}
+.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}
+.glyphicon-sort-by-order:before{content:"\e153"}
+.glyphicon-sort-by-order-alt:before{content:"\e154"}
+.glyphicon-sort-by-attributes:before{content:"\e155"}
+.glyphicon-sort-by-attributes-alt:before{content:"\e156"}
+.glyphicon-unchecked:before{content:"\e157"}
+.glyphicon-expand:before{content:"\e158"}
+.glyphicon-collapse-down:before{content:"\e159"}
+.glyphicon-collapse-up:before{content:"\e160"}
+.glyphicon-log-in:before{content:"\e161"}
+.glyphicon-flash:before{content:"\e162"}
+.glyphicon-log-out:before{content:"\e163"}
+.glyphicon-new-window:before{content:"\e164"}
+.glyphicon-record:before{content:"\e165"}
+.glyphicon-save:before{content:"\e166"}
+.glyphicon-open:before{content:"\e167"}
+.glyphicon-saved:before{content:"\e168"}
+.glyphicon-import:before{content:"\e169"}
+.glyphicon-export:before{content:"\e170"}
+.glyphicon-send:before{content:"\e171"}
+.glyphicon-floppy-disk:before{content:"\e172"}
+.glyphicon-floppy-saved:before{content:"\e173"}
+.glyphicon-floppy-remove:before{content:"\e174"}
+.glyphicon-floppy-save:before{content:"\e175"}
+.glyphicon-floppy-open:before{content:"\e176"}
+.glyphicon-credit-card:before{content:"\e177"}
+.glyphicon-transfer:before{content:"\e178"}
+.glyphicon-cutlery:before{content:"\e179"}
+.glyphicon-header:before{content:"\e180"}
+.glyphicon-compressed:before{content:"\e181"}
+.glyphicon-earphone:before{content:"\e182"}
+.glyphicon-phone-alt:before{content:"\e183"}
+.glyphicon-tower:before{content:"\e184"}
+.glyphicon-stats:before{content:"\e185"}
+.glyphicon-sd-video:before{content:"\e186"}
+.glyphicon-hd-video:before{content:"\e187"}
+.glyphicon-subtitles:before{content:"\e188"}
+.glyphicon-sound-stereo:before{content:"\e189"}
+.glyphicon-sound-dolby:before{content:"\e190"}
+.glyphicon-sound-5-1:before{content:"\e191"}
+.glyphicon-sound-6-1:before{content:"\e192"}
+.glyphicon-sound-7-1:before{content:"\e193"}
+.glyphicon-copyright-mark:before{content:"\e194"}
+.glyphicon-registration-mark:before{content:"\e195"}
+.glyphicon-cloud-download:before{content:"\e197"}
+.glyphicon-cloud-upload:before{content:"\e198"}
+.glyphicon-tree-conifer:before{content:"\e199"}
+.glyphicon-tree-deciduous:before{content:"\e200"}
+.glyphicon-cd:before{content:"\e201"}
+.glyphicon-save-file:before{content:"\e202"}
+.glyphicon-open-file:before{content:"\e203"}
+.glyphicon-level-up:before{content:"\e204"}
+.glyphicon-copy:before{content:"\e205"}
+.glyphicon-paste:before{content:"\e206"}
+.glyphicon-alert:before{content:"\e209"}
+.glyphicon-equalizer:before{content:"\e210"}
+.glyphicon-king:before{content:"\e211"}
+.glyphicon-queen:before{content:"\e212"}
+.glyphicon-pawn:before{content:"\e213"}
+.glyphicon-bishop:before{content:"\e214"}
+.glyphicon-knight:before{content:"\e215"}
+.glyphicon-baby-formula:before{content:"\e216"}
+.glyphicon-tent:before{content:"\26fa"}
+.glyphicon-blackboard:before{content:"\e218"}
+.glyphicon-bed:before{content:"\e219"}
+.glyphicon-apple:before{content:"\f8ff"}
+.glyphicon-erase:before{content:"\e221"}
+.glyphicon-hourglass:before{content:"\231b"}
+.glyphicon-lamp:before{content:"\e223"}
+.glyphicon-duplicate:before{content:"\e224"}
+.glyphicon-piggy-bank:before{content:"\e225"}
+.glyphicon-scissors:before{content:"\e226"}
+.glyphicon-bitcoin:before,.glyphicon-btc:before,.glyphicon-xbt:before{content:"\e227"}
+.glyphicon-jpy:before,.glyphicon-yen:before{content:"\00a5"}
+.glyphicon-rub:before,.glyphicon-ruble:before{content:"\20bd"}
+.glyphicon-scale:before{content:"\e230"}
+.glyphicon-ice-lolly:before{content:"\e231"}
+.glyphicon-ice-lolly-tasted:before{content:"\e232"}
+.glyphicon-education:before{content:"\e233"}
+.glyphicon-option-horizontal:before{content:"\e234"}
+.glyphicon-option-vertical:before{content:"\e235"}
+.glyphicon-menu-hamburger:before{content:"\e236"}
+.glyphicon-modal-window:before{content:"\e237"}
+.glyphicon-oil:before{content:"\e238"}
+.glyphicon-grain:before{content:"\e239"}
+.glyphicon-sunglasses:before{content:"\e240"}
+.glyphicon-text-size:before{content:"\e241"}
+.glyphicon-text-color:before{content:"\e242"}
+.glyphicon-text-background:before{content:"\e243"}
+.glyphicon-object-align-top:before{content:"\e244"}
+.glyphicon-object-align-bottom:before{content:"\e245"}
+.glyphicon-object-align-horizontal:before{content:"\e246"}
+.glyphicon-object-align-left:before{content:"\e247"}
+.glyphicon-object-align-vertical:before{content:"\e248"}
+.glyphicon-object-align-right:before{content:"\e249"}
+.glyphicon-triangle-right:before{content:"\e250"}
+.glyphicon-triangle-left:before{content:"\e251"}
+.glyphicon-triangle-bottom:before{content:"\e252"}
+.glyphicon-triangle-top:before{content:"\e253"}
+.glyphicon-console:before{content:"\e254"}
+.glyphicon-superscript:before{content:"\e255"}
+.glyphicon-subscript:before{content:"\e256"}
+.glyphicon-menu-left:before{content:"\e257"}
+.glyphicon-menu-right:before{content:"\e258"}
+.glyphicon-menu-down:before{content:"\e259"}
+.glyphicon-menu-up:before{content:"\e260"}
+*,:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
+body{font-family:Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333}
+button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}
+a{color:#2a9fd6;text-decoration:none}
+a:focus,a:hover{color:#1d7097;text-decoration:none}
+a:focus{outline:dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}
+.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}
+.img-rounded{border-radius:6px}
+.img-thumbnail{padding:4px;line-height:1.42857143;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}
+.img-circle{border-radius:50%}
+hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}
+.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}
+.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
+[role=button]{cursor:pointer}
+.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:#333}
+.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}
+.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}
+.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}
+.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}
+.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}
+.h1,h1{font-size:36px}
+.h2,h2{font-size:30px}
+.h3,h3{font-size:24px}
+.h4,h4{font-size:18px}
+.h5,h5{font-size:14px}
+.h6,h6{font-size:12px}
+p{margin:0 0 10px}
+.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}
+address,blockquote .small,blockquote footer,blockquote small,dd,dt,pre{line-height:1.42857143}
+dt,kbd kbd,label{font-weight:700}
+@media (min-width:768px){.lead{font-size:21px}
+}
+.small,small{font-size:85%}
+.mark,mark{background-color:#f80;padding:.2em}
+.list-inline,.list-unstyled{list-style:none;padding-left:0}
+.text-left{text-align:left}
+.text-right{text-align:right}
+.text-center{text-align:center}
+.text-justify{text-align:justify}
+.text-nowrap{white-space:nowrap}
+.text-lowercase{text-transform:lowercase}
+.text-uppercase{text-transform:uppercase}
+.text-capitalize{text-transform:capitalize}
+.text-muted{color:#777}
+.text-primary{color:#2a9fd6}
+a.text-primary:focus,a.text-primary:hover{color:#2180ac}
+a.text-success:focus,a.text-success:hover{color:#2b542c}
+a.text-info:focus,a.text-info:hover{color:#245269}
+a.text-warning:focus,a.text-warning:hover{color:#66512c}
+a.text-danger:focus,a.text-danger:hover{color:#843534}
+.bg-primary{color:#fff;background-color:#2a9fd6}
+a.bg-primary:focus,a.bg-primary:hover{background-color:#2180ac}
+.bg-success{background-color:#77b300}
+a.bg-success:focus,a.bg-success:hover{background-color:#558000}
+.bg-info{background-color:#93c}
+a.bg-info:focus,a.bg-info:hover{background-color:#7a29a3}
+.bg-warning{background-color:#f80}
+a.bg-warning:focus,a.bg-warning:hover{background-color:#cc6d00}
+.bg-danger{background-color:#c00}
+a.bg-danger:focus,a.bg-danger:hover{background-color:#900}
+pre code,table{background-color:transparent}
+.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}
+dl,ol,ul{margin-top:0}
+ol,ul{margin-bottom:10px}
+ol ol,ol ul,ul ol,ul ul{margin-bottom:0}
+.list-inline{margin-left:-5px}
+.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}
+dl{margin-bottom:20px}
+dd{margin-left:0}
+@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+.dl-horizontal dd{margin-left:180px}
+}
+abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}
+.initialism{font-size:90%;text-transform:uppercase}
+blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}
+blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}
+blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;color:#777}
+blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}
+.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}
+caption,th{text-align:left}
+code,kbd{padding:2px 4px;font-size:90%}
+.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}
+.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}
+address{margin-bottom:20px;font-style:normal}
+code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}
+code{color:#c7254e;background-color:#f9f2f4;border-radius:4px}
+kbd{color:#333;background-color:#fff;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}
+kbd kbd{padding:0;font-size:100%;box-shadow:none}
+pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}
+.container,.container-fluid{margin-right:auto;margin-left:auto}
+pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;border-radius:0}
+.container,.container-fluid{padding-left:15px;padding-right:15px}
+.pre-scrollable{overflow-y:scroll}
+@media (min-width:768px){.container{width:750px}
+}
+@media (min-width:992px){.container{width:970px}
+}
+@media (min-width:1200px){.container{width:1170px}
+}
+.row{margin-left:-15px;margin-right:-15px}
+.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-left:15px;padding-right:15px}
+.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}
+.col-xs-12{width:100%}
+.col-xs-11{width:91.66666667%}
+.col-xs-10{width:83.33333333%}
+.col-xs-9{width:75%}
+.col-xs-8{width:66.66666667%}
+.col-xs-7{width:58.33333333%}
+.col-xs-6{width:50%}
+.col-xs-5{width:41.66666667%}
+.col-xs-4{width:33.33333333%}
+.col-xs-3{width:25%}
+.col-xs-2{width:16.66666667%}
+.col-xs-1{width:8.33333333%}
+.col-xs-pull-12{right:100%}
+.col-xs-pull-11{right:91.66666667%}
+.col-xs-pull-10{right:83.33333333%}
+.col-xs-pull-9{right:75%}
+.col-xs-pull-8{right:66.66666667%}
+.col-xs-pull-7{right:58.33333333%}
+.col-xs-pull-6{right:50%}
+.col-xs-pull-5{right:41.66666667%}
+.col-xs-pull-4{right:33.33333333%}
+.col-xs-pull-3{right:25%}
+.col-xs-pull-2{right:16.66666667%}
+.col-xs-pull-1{right:8.33333333%}
+.col-xs-pull-0{right:auto}
+.col-xs-push-12{left:100%}
+.col-xs-push-11{left:91.66666667%}
+.col-xs-push-10{left:83.33333333%}
+.col-xs-push-9{left:75%}
+.col-xs-push-8{left:66.66666667%}
+.col-xs-push-7{left:58.33333333%}
+.col-xs-push-6{left:50%}
+.col-xs-push-5{left:41.66666667%}
+.col-xs-push-4{left:33.33333333%}
+.col-xs-push-3{left:25%}
+.col-xs-push-2{left:16.66666667%}
+.col-xs-push-1{left:8.33333333%}
+.col-xs-push-0{left:auto}
+.col-xs-offset-12{margin-left:100%}
+.col-xs-offset-11{margin-left:91.66666667%}
+.col-xs-offset-10{margin-left:83.33333333%}
+.col-xs-offset-9{margin-left:75%}
+.col-xs-offset-8{margin-left:66.66666667%}
+.col-xs-offset-7{margin-left:58.33333333%}
+.col-xs-offset-6{margin-left:50%}
+.col-xs-offset-5{margin-left:41.66666667%}
+.col-xs-offset-4{margin-left:33.33333333%}
+.col-xs-offset-3{margin-left:25%}
+.col-xs-offset-2{margin-left:16.66666667%}
+.col-xs-offset-1{margin-left:8.33333333%}
+.col-xs-offset-0{margin-left:0}
+@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}
+.col-sm-12{width:100%}
+.col-sm-11{width:91.66666667%}
+.col-sm-10{width:83.33333333%}
+.col-sm-9{width:75%}
+.col-sm-8{width:66.66666667%}
+.col-sm-7{width:58.33333333%}
+.col-sm-6{width:50%}
+.col-sm-5{width:41.66666667%}
+.col-sm-4{width:33.33333333%}
+.col-sm-3{width:25%}
+.col-sm-2{width:16.66666667%}
+.col-sm-1{width:8.33333333%}
+.col-sm-pull-12{right:100%}
+.col-sm-pull-11{right:91.66666667%}
+.col-sm-pull-10{right:83.33333333%}
+.col-sm-pull-9{right:75%}
+.col-sm-pull-8{right:66.66666667%}
+.col-sm-pull-7{right:58.33333333%}
+.col-sm-pull-6{right:50%}
+.col-sm-pull-5{right:41.66666667%}
+.col-sm-pull-4{right:33.33333333%}
+.col-sm-pull-3{right:25%}
+.col-sm-pull-2{right:16.66666667%}
+.col-sm-pull-1{right:8.33333333%}
+.col-sm-pull-0{right:auto}
+.col-sm-push-12{left:100%}
+.col-sm-push-11{left:91.66666667%}
+.col-sm-push-10{left:83.33333333%}
+.col-sm-push-9{left:75%}
+.col-sm-push-8{left:66.66666667%}
+.col-sm-push-7{left:58.33333333%}
+.col-sm-push-6{left:50%}
+.col-sm-push-5{left:41.66666667%}
+.col-sm-push-4{left:33.33333333%}
+.col-sm-push-3{left:25%}
+.col-sm-push-2{left:16.66666667%}
+.col-sm-push-1{left:8.33333333%}
+.col-sm-push-0{left:auto}
+.col-sm-offset-12{margin-left:100%}
+.col-sm-offset-11{margin-left:91.66666667%}
+.col-sm-offset-10{margin-left:83.33333333%}
+.col-sm-offset-9{margin-left:75%}
+.col-sm-offset-8{margin-left:66.66666667%}
+.col-sm-offset-7{margin-left:58.33333333%}
+.col-sm-offset-6{margin-left:50%}
+.col-sm-offset-5{margin-left:41.66666667%}
+.col-sm-offset-4{margin-left:33.33333333%}
+.col-sm-offset-3{margin-left:25%}
+.col-sm-offset-2{margin-left:16.66666667%}
+.col-sm-offset-1{margin-left:8.33333333%}
+.col-sm-offset-0{margin-left:0}
+}
+@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}
+.col-md-12{width:100%}
+.col-md-11{width:91.66666667%}
+.col-md-10{width:83.33333333%}
+.col-md-9{width:75%}
+.col-md-8{width:66.66666667%}
+.col-md-7{width:58.33333333%}
+.col-md-6{width:50%}
+.col-md-5{width:41.66666667%}
+.col-md-4{width:33.33333333%}
+.col-md-3{width:25%}
+.col-md-2{width:16.66666667%}
+.col-md-1{width:8.33333333%}
+.col-md-pull-12{right:100%}
+.col-md-pull-11{right:91.66666667%}
+.col-md-pull-10{right:83.33333333%}
+.col-md-pull-9{right:75%}
+.col-md-pull-8{right:66.66666667%}
+.col-md-pull-7{right:58.33333333%}
+.col-md-pull-6{right:50%}
+.col-md-pull-5{right:41.66666667%}
+.col-md-pull-4{right:33.33333333%}
+.col-md-pull-3{right:25%}
+.col-md-pull-2{right:16.66666667%}
+.col-md-pull-1{right:8.33333333%}
+.col-md-pull-0{right:auto}
+.col-md-push-12{left:100%}
+.col-md-push-11{left:91.66666667%}
+.col-md-push-10{left:83.33333333%}
+.col-md-push-9{left:75%}
+.col-md-push-8{left:66.66666667%}
+.col-md-push-7{left:58.33333333%}
+.col-md-push-6{left:50%}
+.col-md-push-5{left:41.66666667%}
+.col-md-push-4{left:33.33333333%}
+.col-md-push-3{left:25%}
+.col-md-push-2{left:16.66666667%}
+.col-md-push-1{left:8.33333333%}
+.col-md-push-0{left:auto}
+.col-md-offset-12{margin-left:100%}
+.col-md-offset-11{margin-left:91.66666667%}
+.col-md-offset-10{margin-left:83.33333333%}
+.col-md-offset-9{margin-left:75%}
+.col-md-offset-8{margin-left:66.66666667%}
+.col-md-offset-7{margin-left:58.33333333%}
+.col-md-offset-6{margin-left:50%}
+.col-md-offset-5{margin-left:41.66666667%}
+.col-md-offset-4{margin-left:33.33333333%}
+.col-md-offset-3{margin-left:25%}
+.col-md-offset-2{margin-left:16.66666667%}
+.col-md-offset-1{margin-left:8.33333333%}
+.col-md-offset-0{margin-left:0}
+}
+@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}
+.col-lg-12{width:100%}
+.col-lg-11{width:91.66666667%}
+.col-lg-10{width:83.33333333%}
+.col-lg-9{width:75%}
+.col-lg-8{width:66.66666667%}
+.col-lg-7{width:58.33333333%}
+.col-lg-6{width:50%}
+.col-lg-5{width:41.66666667%}
+.col-lg-4{width:33.33333333%}
+.col-lg-3{width:25%}
+.col-lg-2{width:16.66666667%}
+.col-lg-1{width:8.33333333%}
+.col-lg-pull-12{right:100%}
+.col-lg-pull-11{right:91.66666667%}
+.col-lg-pull-10{right:83.33333333%}
+.col-lg-pull-9{right:75%}
+.col-lg-pull-8{right:66.66666667%}
+.col-lg-pull-7{right:58.33333333%}
+.col-lg-pull-6{right:50%}
+.col-lg-pull-5{right:41.66666667%}
+.col-lg-pull-4{right:33.33333333%}
+.col-lg-pull-3{right:25%}
+.col-lg-pull-2{right:16.66666667%}
+.col-lg-pull-1{right:8.33333333%}
+.col-lg-pull-0{right:auto}
+.col-lg-push-12{left:100%}
+.col-lg-push-11{left:91.66666667%}
+.col-lg-push-10{left:83.33333333%}
+.col-lg-push-9{left:75%}
+.col-lg-push-8{left:66.66666667%}
+.col-lg-push-7{left:58.33333333%}
+.col-lg-push-6{left:50%}
+.col-lg-push-5{left:41.66666667%}
+.col-lg-push-4{left:33.33333333%}
+.col-lg-push-3{left:25%}
+.col-lg-push-2{left:16.66666667%}
+.col-lg-push-1{left:8.33333333%}
+.col-lg-push-0{left:auto}
+.col-lg-offset-12{margin-left:100%}
+.col-lg-offset-11{margin-left:91.66666667%}
+.col-lg-offset-10{margin-left:83.33333333%}
+.col-lg-offset-9{margin-left:75%}
+.col-lg-offset-8{margin-left:66.66666667%}
+.col-lg-offset-7{margin-left:58.33333333%}
+.col-lg-offset-6{margin-left:50%}
+.col-lg-offset-5{margin-left:41.66666667%}
+.col-lg-offset-4{margin-left:33.33333333%}
+.col-lg-offset-3{margin-left:25%}
+.col-lg-offset-2{margin-left:16.66666667%}
+.col-lg-offset-1{margin-left:8.33333333%}
+.col-lg-offset-0{margin-left:0}
+}
+caption{padding-top:8px;padding-bottom:8px;color:#777}
+.table{width:100%;max-width:100%;margin-bottom:20px;}
+.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}
+.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}
+.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}
+.table>tbody+tbody{border-top:2px solid #ddd}
+.table .table{background-color:#f1f1f1}
+.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}
+.table-bordered,.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}
+.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}
+.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}
+.table-hover>tbody>tr:hover,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5;color:#333}
+table col[class*=col-]{position:static;float:none;display:table-column}
+table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}
+.btn-group>.btn-group,.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group,.dropdown-menu{float:left}
+.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8;color: #777;}
+.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#77b300}
+.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#669a00}
+.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#93c}
+.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#8a2eb8}
+.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#f80}
+.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#e67a00}
+.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#c00}
+.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#b30000}
+.table-responsive{overflow-x:auto;min-height:.01%}
+@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}
+.table-responsive>.table{margin-bottom:0}
+.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}
+.table-responsive>.table-bordered{border:0}
+.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}
+.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}
+.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}
+}
+fieldset,legend{padding:0;border:0}
+fieldset{margin:0;min-width:0}
+legend{display:block;width:100%;margin-bottom:20px;font-size:21px;line-height:inherit;border-bottom:1px solid #e5e5e5}
+label{display:inline-block;max-width:100%;margin-bottom:5px}
+input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none}
+input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}
+.form-control,output{display:block;font-size:14px;line-height:1.42857143;color:#555}
+input[type=file]{display:block}
+input[type=range]{display:block;width:100%}
+select[multiple],select[size]{height:auto}
+input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}
+output{padding-top:7px}
+.form-control{width:100%;height:34px;padding:6px 12px;background-color:#f0f0f0;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}
+.form-control:focus{border-color:#2a9fd6;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(42,159,214,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(42,159,214,.6)}
+.form-control::-moz-placeholder{color:#999;opacity:1}
+.form-control:-ms-input-placeholder{color:#999}
+.form-control::-webkit-input-placeholder{color:#999}
+.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}
+.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}
+textarea.form-control{height:auto}
+@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}
+.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}
+.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}
+}
+.form-group{margin-bottom:15px}
+.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}
+.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}
+.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px;margin-top:4px\9}
+.checkbox+.checkbox,.radio+.radio{margin-top:-5px}
+.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}
+.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}
+.checkbox-inline.disabled,.checkbox.disabled label,.radio-inline.disabled,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio label,fieldset[disabled] .radio-inline,fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}
+.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}
+.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}
+.form-group-sm .form-control,.input-sm{font-size:12px;padding:5px 10px;border-radius:3px}
+.input-sm{height:30px;line-height:1.5}
+select.input-sm{height:30px;line-height:30px}
+select[multiple].input-sm,textarea.input-sm{height:auto}
+.form-group-sm .form-control{height:30px;line-height:1.5}
+.form-group-sm select.form-control{height:30px;line-height:30px}
+.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}
+.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}
+.form-group-lg .form-control,.input-lg{font-size:18px;padding:10px 16px;border-radius:6px}
+.input-lg{height:46px;line-height:1.3333333}
+select.input-lg{height:46px;line-height:46px}
+select[multiple].input-lg,textarea.input-lg{height:auto}
+.form-group-lg .form-control{height:46px;line-height:1.3333333}
+.form-group-lg select.form-control{height:46px;line-height:46px}
+.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}
+.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}
+.has-feedback{position:relative}
+.has-feedback .form-control{padding-right:42.5px}
+.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}
+.collapsing,.dropdown,.dropup{position:relative}
+.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}
+.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}
+.has-success .form-control{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}
+.has-success .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}
+.has-success .input-group-addon{color:#3c763d;background-color:#77b300}
+.has-warning .form-control{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}
+.has-warning .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}
+.has-warning .input-group-addon{color:#8a6d3b;background-color:#f80}
+.has-error .form-control{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}
+.has-error .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}
+.has-error .input-group-addon{color:#a94442;background-color:#c00}
+.has-feedback label~.form-control-feedback{top:25px}
+.has-feedback label.sr-only~.form-control-feedback{top:0}
+.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}
+@media (min-width:768px){.form-inline .form-control-static,.form-inline .form-group{display:inline-block}
+.form-inline .control-label,.form-inline .form-group{margin-bottom:0;vertical-align:middle}
+.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}
+.form-inline .input-group{display:inline-table;vertical-align:middle}
+.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}
+.form-inline .input-group>.form-control{width:100%}
+.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}
+.form-inline .checkbox label,.form-inline .radio label{padding-left:0}
+.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}
+.form-inline .has-feedback .form-control-feedback{top:0}
+}
+.btn-block,input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}
+.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{margin-top:0;margin-bottom:0;padding-top:7px}
+.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}
+.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}
+.form-horizontal .has-feedback .form-control-feedback{right:15px}
+@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}
+.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}
+.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}
+}
+.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
+.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}
+.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}
+.btn.active,.btn:active{outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}
+.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}
+a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}
+.btn-default{color:#333;background-color:#fff;border-color:#ccc}
+.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}
+.btn-default.active,.btn-default:active,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}
+.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}
+.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}
+.btn-default .badge{color:#fff;background-color:#333}
+.btn-primary{color:#fff;background-color:#2a9fd6;border-color:#258fc1}
+.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#2180ac;border-color:#114056}
+.btn-primary.active,.btn-primary:active,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#2180ac;border-color:#1b698e}
+.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#1b698e;border-color:#114056}
+.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#2a9fd6;border-color:#258fc1}
+.btn-primary .badge{color:#2a9fd6;background-color:#fff}
+.btn-success{color:#fff;background-color:#77b300;border-color:#669a00}
+.btn-success.focus,.btn-success:focus{color:#fff;background-color:#558000;border-color:#111a00}
+.btn-success.active,.btn-success:active,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#558000;border-color:#3d5c00}
+.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#3d5c00;border-color:#111a00}
+.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#77b300;border-color:#669a00}
+.btn-success .badge{color:#77b300;background-color:#fff}
+.btn-info{color:#fff;background-color:#93c;border-color:#8a2eb8}
+.btn-info.focus,.btn-info:focus{color:#fff;background-color:#7a29a3;border-color:#3d1452}
+.btn-info.active,.btn-info:active,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#7a29a3;border-color:#652287}
+.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#652287;border-color:#3d1452}
+.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#93c;border-color:#8a2eb8}
+.btn-info .badge{color:#93c;background-color:#fff}
+.btn-warning{color:#fff;background-color:#f80;border-color:#e67a00}
+.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#cc6d00;border-color:#663600}
+.btn-warning.active,.btn-warning:active,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#cc6d00;border-color:#a85a00}
+.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#a85a00;border-color:#663600}
+.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f80;border-color:#e67a00}
+.btn-warning .badge{color:#f80;background-color:#fff}
+.btn-danger{color:#fff;background-color:#c00;border-color:#b30000}
+.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#900;border-color:#300}
+.btn-danger.active,.btn-danger:active,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#900;border-color:#750000}
+.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#750000;border-color:#300}
+.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c00;border-color:#b30000}
+.btn-danger .badge{color:#c00;background-color:#fff}
+.btn-link{color:#2a9fd6;font-weight:400;border-radius:0}
+.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}
+.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}
+.btn-link:focus,.btn-link:hover{color:#1d7097;text-decoration:none;background-color:transparent}
+.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}
+.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}
+.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}
+.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}
+.btn-block{display:block}
+.btn-block+.btn-block{margin-top:5px}
+.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}
+.fade.in{opacity:1}
+.collapse{display:none}
+.collapse.in{display:block}
+tr.collapse.in{display:table-row}
+tbody.collapse.in{display:table-row-group}
+.collapsing{height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}
+.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}
+.dropdown-toggle:focus{outline:0}
+.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}
+.btn-group-vertical>.btn:not(:first-child):not(:last-child),.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn,.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}
+.dropdown-header,.dropdown-menu>li>a{white-space:nowrap;padding:3px 20px;line-height:1.42857143}
+.dropdown-menu-right,.dropdown-menu.pull-right{left:auto;right:0}
+.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}
+.dropdown-menu>li>a{display:block;clear:both;font-weight:400;color:#333}
+.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{text-decoration:none;color:#fff;background-color:#2a9fd6}
+.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;outline:0;background-color:#2a9fd6}
+.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}
+.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}
+.open>.dropdown-menu{display:block}
+.open>a{outline:0}
+.dropdown-menu-left{left:0;right:auto}
+.dropdown-header{display:block;font-size:12px;color:#777}
+.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}
+.nav-justified>.dropdown .dropdown-menu,.nav-tabs.nav-justified>.dropdown .dropdown-menu{left:auto;top:auto}
+.pull-right>.dropdown-menu{right:0;left:auto}
+.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9;content:""}
+.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}
+@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}
+.navbar-right .dropdown-menu-left{left:0;right:auto}
+}
+.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}
+.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}
+.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}
+.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}
+.btn-toolbar{margin-left:-5px}
+.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}
+.btn .caret,.btn-group>.btn:first-child{margin-left:0}
+.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}
+.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}
+.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}
+.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}
+.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}
+.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}
+.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}
+.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}
+.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}
+.btn-lg .caret{border-width:5px 5px 0}
+.dropup .btn-lg .caret{border-width:0 5px 5px}
+.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}
+.btn-group-vertical>.btn-group>.btn{float:none}
+.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}
+.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}
+.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}
+.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}
+.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}
+.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}
+.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}
+.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}
+.btn-group-justified>.btn-group .btn{width:100%}
+.btn-group-justified>.btn-group .dropdown-menu{left:auto}
+[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}
+.input-group{position:relative;display:table;border-collapse:separate}
+.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}
+.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}
+.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}
+select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}
+select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}
+.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}
+select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}
+select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}
+.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}
+.nav>li,.nav>li>a{position:relative;display:block}
+.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}
+.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}
+.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;border:1px solid #ccc;border-radius:4px}
+.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}
+.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}
+.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}
+.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}
+.input-group-addon:first-child{border-right:0}
+.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-bottom-left-radius:0;border-top-left-radius:0}
+.input-group-addon:last-child{border-left:0}
+.input-group-btn{position:relative;font-size:0;white-space:nowrap}
+.input-group-btn>.btn{position:relative}
+.input-group-btn>.btn+.btn{margin-left:-1px}
+.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}
+.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}
+.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}
+.nav{margin-bottom:0;padding-left:0;list-style:none}
+.nav>li>a{padding:10px 15px}
+.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}
+.nav>li.disabled>a{color:#777}
+.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}
+.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#2a9fd6}
+.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}
+.nav>li>a>img{max-width:none}
+.nav-tabs{border-bottom:1px solid #ddd}
+.nav-tabs>li{float:left;margin-bottom:-1px}
+.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}
+.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}
+.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;background-color:#f1f1f1;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}
+.nav-tabs.nav-justified{width:100%;border-bottom:0}
+.nav-tabs.nav-justified>li{float:none}
+.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px;margin-right:0;border-radius:4px}
+.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}
+@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}
+.nav-tabs.nav-justified>li>a{margin-bottom:0;border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}
+.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#f1f1f1}
+}
+.nav-pills>li{float:left}
+.nav-justified>li,.nav-stacked>li{float:none}
+.nav-pills>li>a{border-radius:4px}
+.nav-pills>li+li{margin-left:2px}
+.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#2a9fd6}
+.nav-stacked>li+li{margin-top:2px;margin-left:0}
+.nav-justified{width:100%}
+.nav-justified>li>a{text-align:center;margin-bottom:5px}
+.nav-tabs-justified{border-bottom:0}
+.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}
+.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}
+@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}
+.nav-justified>li>a{margin-bottom:0}
+.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}
+.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#f1f1f1}
+}
+.tab-content>.tab-pane{display:none}
+.tab-content>.active{display:block}
+.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}
+.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}
+.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}
+.navbar-collapse.in{overflow-y:auto}
+@media (min-width:768px){.navbar{border-radius:4px}
+.navbar-header{float:left}
+.navbar-collapse{width:auto;border-top:0;box-shadow:none}
+.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}
+.navbar-collapse.in{overflow-y:visible}
+.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-left:0;padding-right:0}
+}
+.embed-responsive,.modal,.modal-open,.progress{overflow:hidden}
+@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}
+}
+.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}
+.navbar-static-top{z-index:1000;border-width:0 0 1px}
+.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}
+.navbar-fixed-top{top:0;border-width:0 0 1px}
+.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}
+.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}
+.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}
+.navbar-brand>img{display:block}
+@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}
+.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}
+.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}
+}
+.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;border:1px solid transparent;border-radius:4px}
+.navbar-toggle:focus{outline:0}
+.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}
+.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}
+.navbar-nav{margin:7.5px -15px}
+.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}
+@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}
+.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}
+.navbar-nav .open .dropdown-menu>li>a{line-height:20px}
+.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}
+}
+.progress-bar-striped,.progress-striped .progress-bar,.progress-striped .progress-bar-danger,.progress-striped .progress-bar-info,.progress-striped .progress-bar-success,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}
+@media (min-width:768px){.navbar-toggle{display:none}
+.navbar-nav{float:left;margin:0}
+.navbar-nav>li{float:left}
+.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}
+}
+.navbar-form{padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin:8px -15px}
+@media (min-width:768px){.navbar-form .form-control-static,.navbar-form .form-group{display:inline-block}
+.navbar-form .control-label,.navbar-form .form-group{margin-bottom:0;vertical-align:middle}
+.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}
+.navbar-form .input-group{display:inline-table;vertical-align:middle}
+.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}
+.navbar-form .input-group>.form-control{width:100%}
+.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}
+.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}
+.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}
+.navbar-form .has-feedback .form-control-feedback{top:0}
+}
+.btn .badge,.btn .label{position:relative;top:-1px}
+.breadcrumb>li,.pagination{display:inline-block}
+@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}
+.navbar-form .form-group:last-child{margin-bottom:0}
+}
+@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}
+}
+.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}
+.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-radius:4px 4px 0 0}
+.navbar-btn{margin-top:8px;margin-bottom:8px}
+.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}
+.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}
+.navbar-text{margin-top:15px;margin-bottom:15px}
+@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}
+.navbar-left{float:left!important}
+.navbar-right{float:right!important;margin-right:-15px}
+.navbar-right~.navbar-right{margin-right:0}
+}
+.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}
+.navbar-default .navbar-brand{color:#000}
+.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}
+.navbar-default .navbar-nav>li>a,.navbar-default .navbar-text{color:#2a9fd6}
+.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#fff;background-color:#2a9fd6}
+.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#fff;background-color:#2a9fd6}
+.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}
+.navbar-default .navbar-toggle{border-color:#2a9fd6}
+.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#2a9fd6}
+.navbar-default .navbar-toggle .icon-bar{background-color:#2a9fd6}
+.navbar-default .navbar-toggle:hover .icon-bar, .navbar-default .navbar-toggle:focus .icon-bar{background-color:#fff}
+.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}
+.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{background-color:#2a9fd6;color:#fff}
+@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}
+.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}
+.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}
+.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}
+}
+.navbar-default .navbar-link{color:#777}
+.navbar-default .navbar-link:hover{color:#333}
+.navbar-default .btn-link{color:#777}
+.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}
+.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}
+.navbar-inverse{background-color:#2A9FD6;border-color:#258fc1}
+.navbar-inverse .navbar-brand{color:#fff}
+.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}
+.navbar-inverse .navbar-nav>li>a,.navbar-inverse .navbar-text{color:#fff}
+.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}
+.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#258fc1}
+.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}
+.navbar-inverse .navbar-toggle{border-color:#333}
+.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}
+.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}
+.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}
+.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{background-color:#258fc1;color:#fff}
+@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#258fc1}
+.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#258fc1}
+.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}
+.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}
+.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}
+.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}
+}
+.navbar-inverse .navbar-link{color:#9d9d9d}
+.navbar-inverse .navbar-link:hover{color:#fff}
+.navbar-inverse .btn-link{color:#9d9d9d}
+.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}
+.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}
+.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}
+.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}
+.breadcrumb>.active{color:#777}
+.pagination{padding-left:0;margin:20px 0;border-radius:4px}
+.pager li,.pagination>li{display:inline}
+.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#2a9fd6;background-color:#fff;border:1px solid #ddd;margin-left:-1px}
+.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}
+.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}
+.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#1d7097;background-color:#eee;border-color:#ddd}
+.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;background-color:#2a9fd6;border-color:#2a9fd6;cursor:default}
+.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}
+.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}
+.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}
+.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}
+.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}
+.badge,.label{text-align:center;font-weight:700;line-height:1;white-space:nowrap}
+.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}
+.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}
+.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}
+.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}
+.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}
+.pager .next>a,.pager .next>span{float:right}
+.pager .previous>a,.pager .previous>span{float:left}
+.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}
+.label{display:inline;padding:.2em .6em .3em;font-size:75%;color:#fff;vertical-align:baseline;border-radius:.25em}
+a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}
+.label:empty{display:none}
+.label-default{background-color:#777}
+.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}
+.label-primary{background-color:#2a9fd6}
+.label-primary[href]:focus,.label-primary[href]:hover{background-color:#2180ac}
+.label-success{background-color:#77b300}
+.label-success[href]:focus,.label-success[href]:hover{background-color:#558000}
+.label-info{background-color:#93c}
+.label-info[href]:focus,.label-info[href]:hover{background-color:#7a29a3}
+.label-warning{background-color:#f80}
+.label-warning[href]:focus,.label-warning[href]:hover{background-color:#cc6d00}
+.label-danger{background-color:#c00}
+.label-danger[href]:focus,.label-danger[href]:hover{background-color:#900}
+.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;color:#fff;vertical-align:middle;background-color:#777;border-radius:10px}
+.badge:empty{display:none}
+.media-object,.thumbnail{display:block}
+.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}
+a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}
+.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2a9fd6;background-color:#fff}
+.list-group-item>.badge{float:right}
+.list-group-item>.badge+.badge{margin-right:5px}
+.nav-pills>li>a>.badge{margin-left:3px}
+.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}
+.jumbotron .h1,.jumbotron h1{color:#333}
+.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}
+.alert .alert-link,.close{font-weight:700}
+.alert,.thumbnail{margin-bottom:20px}
+.jumbotron>hr{border-top-color:#d5d5d5}
+.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}
+.jumbotron .container{max-width:100%}
+@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}
+.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}
+.jumbotron .h1,.jumbotron h1{font-size:63px}
+}
+.thumbnail{padding:4px;line-height:1.42857143;background-color:#f1f1f1;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}
+.thumbnail a>img,.thumbnail>img{margin-left:auto;margin-right:auto}
+.thumbnail .caption{padding:9px;color:#333}
+.alert{padding:15px;border:1px solid transparent;border-radius:4px}
+.alert h4{margin-top:0;color:inherit}
+.alert>p,.alert>ul{margin-bottom:0}
+.alert>p+p{margin-top:5px}
+.alert-dismissable,.alert-dismissible{padding-right:35px}
+.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}
+.modal,.modal-backdrop{right:0;bottom:0;left:0}
+.alert-success{background-color:#77b300;border-color:#809a00;color:#fff}
+.alert-success hr{border-top-color:#6a8000}
+.alert-success .alert-link{color:#e6e6e6}
+.alert-info{background-color:#93c;border-color:#6e2caf;color:#fff}
+.alert-info hr{border-top-color:#61279b}
+.alert-info .alert-link{color:#e6e6e6}
+.alert-warning{background-color:#f80;border-color:#e65400;color:#fff}
+.alert-warning hr{border-top-color:#cc4b00}
+.alert-warning .alert-link{color:#e6e6e6}
+.alert-danger{background-color:#c00;border-color:#b3001e;color:#fff}
+.alert-danger hr{border-top-color:#99001a}
+.alert-danger .alert-link{color:#e6e6e6}
+@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}
+to{background-position:0 0}
+}
+@keyframes progress-bar-stripes{from{background-position:40px 0}
+to{background-position:0 0}
+}
+.progress{height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}
+.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#2a9fd6;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}
+.progress-bar-striped,.progress-striped .progress-bar{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}
+.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}
+.progress-bar-success{background-color:#77b300}
+.progress-striped .progress-bar-success{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}
+.progress-bar-info{background-color:#93c}
+.progress-striped .progress-bar-info{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}
+.progress-bar-warning{background-color:#f80}
+.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}
+.progress-bar-danger{background-color:#c00}
+.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}
+.media{margin-top:15px}
+.media:first-child{margin-top:0}
+.media,.media-body{zoom:1;overflow:hidden}
+.media-body{width:10000px}
+.media-object.img-thumbnail{max-width:none}
+.media-right,.media>.pull-right{padding-left:10px}
+.media-left,.media>.pull-left{padding-right:10px}
+.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}
+.media-middle{vertical-align:middle}
+.media-bottom{vertical-align:bottom}
+.media-heading{margin-top:0;margin-bottom:5px}
+.media-list{padding-left:0;list-style:none}
+.list-group{margin-bottom:20px;padding-left:0}
+.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}
+.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}
+.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}
+a.list-group-item,button.list-group-item{color:#555}
+a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}
+a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{text-decoration:none;color:#555;background-color:#f5f5f5}
+button.list-group-item{width:100%;text-align:left}
+.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{background-color:#eee;color:#777;cursor:not-allowed}
+.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}
+.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}
+.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#2a9fd6;border-color:#2a9fd6}
+.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}
+.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#d5ecf7}
+.list-group-item-success{color:#3c763d;background-color:#77b300}
+a.list-group-item-success,button.list-group-item-success{color:#3c763d}
+a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}
+a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#669a00}
+a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}
+.list-group-item-info{color:#31708f;background-color:#93c}
+a.list-group-item-info,button.list-group-item-info{color:#31708f}
+a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}
+a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#8a2eb8}
+a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}
+.list-group-item-warning{color:#8a6d3b;background-color:#f80}
+a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}
+a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}
+a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#e67a00}
+a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}
+.list-group-item-danger{color:#a94442;background-color:#c00}
+a.list-group-item-danger,button.list-group-item-danger{color:#a94442}
+a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}
+a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#b30000}
+a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}
+.panel-heading>.dropdown .dropdown-toggle,.panel-title,.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}
+.list-group-item-heading{margin-top:0;margin-bottom:5px}
+.list-group-item-text{margin-bottom:0;line-height:1.3}
+.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}
+.panel-title,.panel>.list-group,.panel>.panel-collapse>.list-group,.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}
+.panel-body{padding:15px}
+.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}
+.panel-group .panel-heading,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}
+.panel-title{margin-top:0;font-size:16px}
+.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}
+.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}
+.panel>.table-responsive:last-child>.table:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}
+.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}
+.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}
+.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}
+.panel>.table-responsive:first-child>.table:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-right-radius:3px;border-top-left-radius:3px}
+.list-group+.panel-footer,.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}
+.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-left:15px;padding-right:15px}
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}
+.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}
+.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}
+.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}
+.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}
+.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}
+.panel>.table-responsive{border:0;margin-bottom:0}
+.panel-group{margin-bottom:20px}
+.panel-group .panel{margin-bottom:0;border-radius:4px}
+.panel-group .panel+.panel{margin-top:5px}
+.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}
+.panel-group .panel-footer{border-top:0}
+.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}
+.panel-default{border-color:#ddd}
+.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}
+.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}
+.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}
+.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}
+.panel-primary{border-color:#2a9fd6}
+.panel-primary>.panel-heading{color:#fff;background-color:#2a9fd6;border-color:#2a9fd6}
+.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2a9fd6}
+.panel-primary>.panel-heading .badge{color:#2a9fd6;background-color:#fff}
+.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2a9fd6}
+.panel-success{border-color:#809a00}
+.panel-success>.panel-heading{color:#fff;background-color:#77b300;border-color:#809a00}
+.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#809a00}
+.panel-success>.panel-heading .badge{color:#77b300;background-color:#fff}
+.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#809a00}
+.panel-info{border-color:#6e2caf}
+.panel-info>.panel-heading{color:#fff;background-color:#93c;border-color:#6e2caf}
+.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#6e2caf}
+.panel-info>.panel-heading .badge{color:#93c;background-color:#fff}
+.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#6e2caf}
+.panel-warning{border-color:#e65400}
+.panel-warning>.panel-heading{color:#fff;background-color:#f80;border-color:#e65400}
+.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e65400}
+.panel-warning>.panel-heading .badge{color:#f80;background-color:#fff}
+.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e65400}
+.panel-danger{border-color:#b3001e}
+.panel-danger>.panel-heading{color:#fff;background-color:#c00;border-color:#b3001e}
+.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#b3001e}
+.panel-danger>.panel-heading .badge{color:#c00;background-color:#fff}
+.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#b3001e}
+.embed-responsive{position:relative;display:block;height:0;padding:0}
+.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}
+.embed-responsive-16by9{padding-bottom:56.25%}
+.embed-responsive-4by3{padding-bottom:75%}
+.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}
+.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}
+.well-lg{padding:24px;border-radius:6px}
+.well-sm{padding:9px;border-radius:3px}
+.close{float:right;font-size:21px;line-height:1;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20)}
+.popover,.tooltip{text-decoration:none;font-family:Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal}
+.close:focus,.close:hover{text-decoration:none;cursor:pointer;filter:alpha(opacity=50)}
+button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}
+.modal-content,.popover{background-clip:padding-box}
+.modal{display:none;position:fixed;top:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}
+.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}
+.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}
+.modal-open .modal{overflow-x:hidden;overflow-y:auto}
+.modal-dialog{position:relative;width:auto;margin:10px}
+.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0}
+.modal-backdrop{position:fixed;top:0;z-index:1040;background-color:#000}
+.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}
+.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}
+.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.43px}
+.tooltip.bottom .tooltip-arrow,.tooltip.bottom-left .tooltip-arrow,.tooltip.bottom-right .tooltip-arrow{top:0;border-width:0 5px 5px;border-bottom-color:#000}
+.modal-header .close{margin-top:-2px}
+.modal-title{margin:0;line-height:1.42857143}
+.modal-body{position:relative;padding:15px}
+.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}
+.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}
+.modal-footer .btn-group .btn+.btn{margin-left:-1px}
+.modal-footer .btn-block+.btn-block{margin-left:0}
+.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}
+@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}
+.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}
+.modal-sm{width:300px}
+}
+.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}
+@media (min-width:992px){.modal-lg{width:900px}
+}
+.tooltip{position:absolute;z-index:1070;display:block;text-align:left;text-align:start;font-size:12px;opacity:0;filter:alpha(opacity=0)}
+.tooltip.in{opacity:.9;filter:alpha(opacity=90)}
+.tooltip.top{margin-top:-3px;padding:5px 0}
+.tooltip.right{margin-left:3px;padding:0 5px}
+.tooltip.bottom{margin-top:3px;padding:5px 0}
+.tooltip.left{margin-left:-3px;padding:0 5px}
+.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}
+.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}
+.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}
+.tooltip.top-left .tooltip-arrow{right:5px}
+.tooltip.top-right .tooltip-arrow{left:5px}
+.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}
+.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}
+.tooltip.bottom .tooltip-arrow{left:50%;margin-left:-5px}
+.tooltip.bottom-left .tooltip-arrow{right:5px;margin-top:-5px}
+.tooltip.bottom-right .tooltip-arrow{left:5px;margin-top:-5px}
+.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;text-align:start;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}
+.carousel-caption,.carousel-control{color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.6)}
+.popover.top{margin-top:-10px}
+.popover.right{margin-left:10px}
+.popover.bottom{margin-top:10px}
+.popover.left{margin-left:-10px}
+.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}
+.popover-content{padding:9px 14px}
+.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}
+.carousel,.carousel-inner{position:relative}
+.popover>.arrow{border-width:11px}
+.popover>.arrow:after{border-width:10px;content:""}
+.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}
+.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}
+.popover.left>.arrow:after,.popover.right>.arrow:after{content:" ";bottom:-10px}
+.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}
+.popover.right>.arrow:after{left:1px;border-left-width:0;border-right-color:#fff}
+.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}
+.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}
+.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}
+.popover.left>.arrow:after{right:1px;border-right-width:0;border-left-color:#fff}
+.carousel-inner{overflow:hidden;width:100%}
+.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}
+.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}
+@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-moz-transition:-moz-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}
+.carousel-inner>.item.active.right,.carousel-inner>.item.next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);left:0}
+.carousel-inner>.item.active.left,.carousel-inner>.item.prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);left:0}
+.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);left:0}
+}
+.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}
+.carousel-inner>.active{left:0}
+.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}
+.carousel-inner>.next{left:100%}
+.carousel-inner>.prev{left:-100%}
+.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}
+.carousel-inner>.active.left{left:-100%}
+.carousel-inner>.active.right{left:100%}
+.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;text-align:center}
+.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}
+.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}
+.carousel-control:focus,.carousel-control:hover{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}
+.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}
+.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}
+.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}
+.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;line-height:1;font-family:serif}
+.carousel-control .icon-prev:before{content:'\2039'}
+.carousel-control .icon-next:before{content:'\203a'}
+.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}
+.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000\9;background-color:transparent}
+.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}
+.table-responsive>.table,.text-hide{background-color:transparent}
+.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;text-align:center}
+.carousel-caption .btn,.text-hide{text-shadow:none}
+@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}
+.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}
+.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}
+.carousel-caption{left:20%;right:20%;padding-bottom:30px}
+.carousel-indicators{bottom:20px}
+}
+.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{content:" ";display:table}
+.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}
+.center-block{display:block;margin-left:auto;margin-right:auto}
+.pull-right{float:right!important}
+.pull-left{float:left!important}
+.hide{display:none!important}
+.show{display:block!important}
+.hidden,.visible-lg,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}
+.invisible{visibility:hidden}
+.text-hide{font:0/0 a;color:transparent;border:0}
+.affix{position:fixed}
+@-ms-viewport{width:device-width}
+@media (max-width:767px){.visible-xs{display:block!important}
+table.visible-xs{display:table!important}
+tr.visible-xs{display:table-row!important}
+td.visible-xs,th.visible-xs{display:table-cell!important}
+.visible-xs-block{display:block!important}
+.visible-xs-inline{display:inline!important}
+.visible-xs-inline-block{display:inline-block!important}
+}
+@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}
+table.visible-sm{display:table!important}
+tr.visible-sm{display:table-row!important}
+td.visible-sm,th.visible-sm{display:table-cell!important}
+.visible-sm-block{display:block!important}
+.visible-sm-inline{display:inline!important}
+.visible-sm-inline-block{display:inline-block!important}
+}
+@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}
+table.visible-md{display:table!important}
+tr.visible-md{display:table-row!important}
+td.visible-md,th.visible-md{display:table-cell!important}
+.visible-md-block{display:block!important}
+.visible-md-inline{display:inline!important}
+.visible-md-inline-block{display:inline-block!important}
+}
+@media (min-width:1200px){.visible-lg{display:block!important}
+table.visible-lg{display:table!important}
+tr.visible-lg{display:table-row!important}
+td.visible-lg,th.visible-lg{display:table-cell!important}
+.visible-lg-block{display:block!important}
+.visible-lg-inline{display:inline!important}
+.visible-lg-inline-block{display:inline-block!important}
+}
+@media (max-width:767px){.hidden-xs{display:none!important}
+}
+@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}
+}
+@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}
+}
+@media (min-width:1200px){.hidden-lg{display:none!important}
+}
+.visible-print{display:none!important}
+@media print{.visible-print{display:block!important}
+table.visible-print{display:table!important}
+tr.visible-print{display:table-row!important}
+td.visible-print,th.visible-print{display:table-cell!important}
+}
+.visible-print-block{display:none!important}
+@media print{.visible-print-block{display:block!important}
+}
+.visible-print-inline{display:none!important}
+@media print{.visible-print-inline{display:inline!important}
+}
+.visible-print-inline-block{display:none!important}
+@media print{.visible-print-inline-block{display:inline-block!important}
+.hidden-print{display:none!important}
+}
+.text-primary,.text-primary:hover{color:#2a9fd6}
+.text-success,.text-success:hover{color:#77b300}
+.text-danger,.text-danger:hover{color:#c00}
+.text-warning,.text-warning:hover{color:#f80}
+.text-info,.text-info:hover{color:#93c}
+.bg-danger,.bg-info,.bg-success,.bg-warning{color:#fff}
+.table a:not(.btn),table a:not(.btn){color:#333;text-decoration:none}
+.table .dropdown-menu a,table .dropdown-menu a{text-decoration:none}
+.table .text-muted,table .text-muted{color:#777}
+.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .form-control-feedback,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#f80}
+.has-warning .form-control,.has-warning .form-control:focus,.has-warning .input-group-addon{border-color:#f80}
+.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .form-control-feedback,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#c00}
+.has-error .form-control,.has-error .form-control:focus,.has-error .input-group-addon{border-color:#c00}
+.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .form-control-feedback,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#77b300}
+.breadcrumb a,.close,.close:focus,.close:hover,.jumbotron h1,.jumbotron h2,.jumbotron h3,.jumbotron h4,.jumbotron h5,.jumbotron h6,.nav-pills a,.nav-tabs a,.pager a,legend{color:#333}
+.has-success .form-control,.has-success .form-control:focus,.has-success .input-group-addon{border-color:#77b300}
+.input-group-addon{background-color:#fff}
+.alert .alert-link,.alert a{color:#fff;text-decoration:none}
+.alert .close,.close{text-decoration:none}
+.close{opacity:.4}
+.close:focus,.close:hover{opacity:1}
+a.list-group-item.active,a.list-group-item.active:focus,a.list-group-item.active:hover,a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#ddd}
+a.list-group-item-success.active{background-color:#77b300}
+a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{background-color:#669a00}
+a.list-group-item-warning.active{background-color:#f80}
+a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{background-color:#e67a00}
+a.list-group-item-danger.active{background-color:#c00}
+a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{background-color:#b30000}
\ No newline at end of file
diff --git a/e107_tests/tests/_data/basic-light/maw/index.html b/e107_tests/tests/_data/basic-light/maw/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/tests/_data/basic-light/maw/js/ie10-viewport-bug-workaround.js b/e107_tests/tests/_data/basic-light/maw/js/ie10-viewport-bug-workaround.js
new file mode 100644
index 000000000..7f53b6145
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/maw/js/ie10-viewport-bug-workaround.js
@@ -0,0 +1,22 @@
+/*!
+ * IE10 viewport hack for Surface/desktop Windows 8 bug
+ * Copyright 2014 Twitter, Inc.
+ * Licensed under the Creative Commons Attribution 3.0 Unported License. For
+ * details, see http://creativecommons.org/licenses/by/3.0/.
+ */
+
+// See the Getting Started docs for more information:
+// http://getbootstrap.com/getting-started/#support-ie10-width
+
+(function () {
+ 'use strict';
+ if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
+ var msViewportStyle = document.createElement('style')
+ msViewportStyle.appendChild(
+ document.createTextNode(
+ '@-ms-viewport{width:auto!important}'
+ )
+ )
+ document.querySelector('head').appendChild(msViewportStyle)
+ }
+})();
diff --git a/e107_tests/tests/_data/basic-light/maw/js/index.html b/e107_tests/tests/_data/basic-light/maw/js/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/tests/_data/basic-light/preview.jpg b/e107_tests/tests/_data/basic-light/preview.jpg
new file mode 100644
index 000000000..f29699e21
Binary files /dev/null and b/e107_tests/tests/_data/basic-light/preview.jpg differ
diff --git a/e107_tests/tests/_data/basic-light/style.css b/e107_tests/tests/_data/basic-light/style.css
new file mode 100644
index 000000000..4c0f7fe36
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/style.css
@@ -0,0 +1,204 @@
+@import url(http://fonts.googleapis.com/css?family=Geo);
+/* CUSTOMIZE THE NAVBAR
+-------------------------------------------------- */
+
+body {
+ font-size: 16px;
+}
+.navbar-wrapper {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 20;
+}
+.navbar-wrapper > .container {
+ padding-right: 0;
+ padding-left: 0;
+}
+.navbar-wrapper .navbar {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.navbar-wrapper .navbar .container {
+ width: auto;
+}
+
+/* CUSTOMIZE THE CAROUSEL
+-------------------------------------------------- */
+
+.carousel {
+ margin-bottom: 30px;
+ overflow: hidden;
+ width: 100%;
+}
+.carousel-inner,
+.carousel .item {
+ overflow: hidden;
+}
+.carousel-caption {
+ z-index: 10;
+}
+.carousel-inner > .item > .slader-img {
+ position: relative;
+ width: 100%;
+}
+.carousel-indicators li {
+ border: 2px solid #2a9fd6;
+}
+.carousel-indicators li:hover,
+.carousel-indicators li.active {
+ background-color: #2a9fd6;
+}
+.carousel-control {
+ color: #2a9fd6!important;
+}
+/* CONTENT
+-------------------------------------------------- */
+
+ul.nav-list {
+ border-top: 1px solid #ddd;
+}
+ul.nav-list li {
+ border-top: 1px solid #ddd;
+}
+ul.nav-list li a:hover {
+ background: #eee;
+}
+.menus.panel,
+.article.panel,
+.view-item.panel {
+ padding: 15px;
+}
+.header {
+ background: #3f51b5;
+ height: 95px;
+ margin-bottom: 30px;
+ display: block;
+}
+.n-img {
+ padding-left: 0;
+}
+.article .info,
+.view-item .info {
+ font-size: 12px;
+ padding: 10px;
+ background: #ddd;
+}
+.article .options {
+ text-align: center;
+}
+.view-item .article-body,
+.news-images-1 .col-md-6,
+.news-images-2 .col-md-6{
+ margin-bottom: 30px;
+}
+.comment-box-left {
+ margin-bottom: 15px;
+}
+#comment {
+ margin-bottom: 5px;
+}
+.menus.other_news .othernews-block {
+ margin-bottom: 0;
+}
+.menus .blogcalendar-block {
+ margin-bottom: 0;
+}
+.footer-right {
+ text-align: center;
+ padding-bottom: 30px;
+ font-size: 12px;
+}
+.menus .text ul:not(.media-list) {
+ list-style: inside;
+ padding-left: 0;
+}
+.list-unstyled li,
+.dropdown-menu li{
+ list-style: none;
+}
+.menus-caption {
+ margin-bottom: 15px;
+ color: #2A9FD6;
+}
+#copyright {
+ font-family: 'Geo', sans-serif;
+ background-color: rgba(100, 100, 100, 0.2);
+ padding: 2px 5px;
+ color: #aaa;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ text-decoration: none;
+ letter-spacing: -0.5px;
+}
+#copyright:hover {
+ background-color: #444;
+ color: #fff;
+}
+/* FORUM
+-------------------------------------*/
+#forum-viewtopic li.forum-viewtopic-post { border-top:1px solid #ddd; padding:15px 10px }
+#forum-viewtopic li.forum-viewtopic-post:nth-child(odd) { background: #f1f1f1 }
+#forum, #forum-stats, #forum-rules { min-height: 0px; }
+.forum-thread-text {
+ background: rgba(33,150,243,0.06);
+ border: 1px solid #ddd;
+ padding: 10px;
+}
+.label.label-info {
+ overflow: hidden;
+ display: table;
+ margin: 5px auto;
+ text-align: center;
+ padding: 0 10px;
+ line-height: 2;
+}
+.label.label-info.smalltext {
+ display: block;
+ padding: 0 2px;
+}
+.label-info:after {
+ content: '';
+ display: block;
+ clear: both;
+ visibility: hidden;
+}
+
+/* RESPONSIVE CSS
+-------------------------------------------------- */
+
+@media (max-width: 992px) {
+.n-img {
+ padding-right: 0;
+ margin-bottom: 20px;
+}
+
+}
+
+@media (min-width: 768px) {
+ /* Navbar positioning foo */
+ .navbar-wrapper {
+ margin-top: 20px;
+ }
+ .navbar-wrapper .container {
+ padding-right: 15px;
+ padding-left: 15px;
+ }
+ .navbar-wrapper .navbar {
+ padding-right: 0;
+ padding-left: 0;
+ }
+ .navbar-wrapper .navbar {
+ border-radius: 4px;
+ }
+
+}
+
+
+@media (max-width: 768px) {
+.carousel {
+ margin-top: 50px;
+}
+
+}
diff --git a/e107_tests/tests/_data/basic-light/templates/index.html b/e107_tests/tests/_data/basic-light/templates/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/e107_tests/tests/_data/basic-light/templates/news/news_menu_template.php b/e107_tests/tests/_data/basic-light/templates/news/news_menu_template.php
new file mode 100644
index 000000000..6dc8da5cc
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/templates/news/news_menu_template.php
@@ -0,0 +1,95 @@
+';
+$NEWS_MENU_TEMPLATE['category']['end'] = '';
+$NEWS_MENU_TEMPLATE['category']['item'] = '
+
';
+
+
+// XXX The ListStyle template offers a listed summary of items with a minimum of 10 items per page.
+// As displayed by news.php?cat.1 OR news.php?all
+// {NEWSBODY} should not appear in the LISTSTYLE as it is NOT the same as what would appear on news.php (no query)
+
+// Template/CSS to be reviewed for best bootstrap implementation
+$NEWS_TEMPLATE['list']['caption'] = '{NEWSCATEGORY}';
+$NEWS_TEMPLATE['list']['start'] = '{SETIMAGE: w=400&h=350&crop=1}';
+$NEWS_TEMPLATE['list']['end'] = '';
+$NEWS_TEMPLATE['list']['item'] = '
+
+
+
+';
+
+
+
+
+
+
+//$NEWS_MENU_TEMPLATE['list']['separator'] = ' ';
+
+
+
+// XXX As displayed by news.php (no query) or news.php?list.1.1 (ie. regular view of a particular category)
+//XXX TODO GEt this looking good in the default Bootstrap theme.
+/*
+$NEWS_TEMPLATE['default']['item'] = '
+ {SETIMAGE: w=400}
+
+';
+// News Categories Menu
+define('NEWSCAT_COLS',false);
+// Othernew + Othernus2
+define('OTHERNEWS_COLS',false); // no tables, only divs.
+define('OTHERNEWS_LIMIT', 10); // Limit to 5.
+define('OTHERNEWS2_COLS',false); // no tables, only divs.
+define('OTHERNEWS2_LIMIT', 5); // Limit to 5.
+// Comments
+define('COMMENTLINK', e107::getParser()->toGlyph('fa-comment'));
+define('COMMENTOFFSTRING', '');
+// Personal Messages Menu
+define("PM_INBOX_ICON", "");
+define("PM_OUTBOX_ICON", "");
+define("BULLET", e107::getParser()->toGlyph('fa-check'));
+// Table style
+function tablestyle($caption, $text, $mode){
+ global $style;
+ switch ($style) {
+ case "no-caption":
+ echo "
$text
\n";
+ break;
+ default:
+ echo "
$caption
\n
$text
\n";
+ break;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/e107_tests/tests/_data/basic-light/theme.xml b/e107_tests/tests/_data/basic-light/theme.xml
new file mode 100644
index 000000000..26854bf02
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/theme.xml
@@ -0,0 +1,28 @@
+
+
+
+ Basic Bootstrap 3 Theme
+ Basic Bootstrap 3 theme for e107 v.2
+ generic
+ mcpeace
+ HTML5, CSS3
+
+ preview_frontend.png
+
+
+ bootstrap
+ light
+
+
+
+
+ FRONTPAGE
+
+
+ news.php?extend page.php? contact.php /download /gallery /chatbox_menu /pm /log
+
+
+ /forum /blogcalendar_menu /calendar_menu
+
+
+
\ No newline at end of file
diff --git a/e107_tests/tests/_data/basic-light/theme_config.php b/e107_tests/tests/_data/basic-light/theme_config.php
new file mode 100644
index 000000000..6e80984de
--- /dev/null
+++ b/e107_tests/tests/_data/basic-light/theme_config.php
@@ -0,0 +1,93 @@
+set('sitetheme_pref', $theme_pref);
+ return $pref->dataHasChanged();
+ }
+
+
+ function config()
+ {
+
+ return $var;
+ }
+
+
+ function help()
+ {
+ $text = '
+
+
+
BASIC LIGHT
+
e107 Bootstrap 3 Theme v.1 - March 2016 - for e107 v.2.
+
+
+
+
+
License
+
The MIT License (MIT)
+
+ Copyright (c) 2013 Thomas Park
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
Installation
+
Very Important: before to upload the theme, download and install latest e107 v.2 from: https://github.com/e107inc/e107
+ Extract the theme from zip archive. If you use Winrar click on "Extract here". In the theme folder you must have files and folders like theme.php, theme.xml ... and folders like images, linguages ...
+ After latest e107 v.2 installation, uplod the theme folder on your server in "e107_themes" folder via FTP. If the theme do not works, you have a corupted file, so upload again the theme.
+
+
+
+
+
Theme Javascript Framework
+
The theme use jQuery but it is not loaded by the theme. To load it you need to go in Admin Area > Preferences > Javascript Faramework and set JQUERY (local) to AUTO and PROTOTYPE (local) to DISABLED.
+
+
+
Theme Compatibility
+
This theme is compatible with e107 v.2 and up and was tested in IE10+11, FF and Chrome latest versions. The theme use HTML5, CSS3, jQuery and is not working OK in old browsers with not support for HTML5 and CSS3 or javascript.
+
For more IE compatibility you can add in Admin Area > Meta Tags this meta tag:
+
If you want more complex themes or an unique looking theme for your site please visit Man at Work website where you can buy the best Premium Themes or you can order your best Custom Unique Theme for e107.
Go to Admin area [/html]');
+/*!40000 ALTER TABLE `e107_generic` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_links`
+--
+
+DROP TABLE IF EXISTS `e107_links`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_links` (
+ `link_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `link_name` varchar(100) NOT NULL DEFAULT '',
+ `link_url` varchar(200) NOT NULL DEFAULT '',
+ `link_description` text NOT NULL,
+ `link_button` varchar(100) NOT NULL DEFAULT '',
+ `link_category` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `link_order` int(10) unsigned NOT NULL DEFAULT '0',
+ `link_parent` int(10) unsigned NOT NULL DEFAULT '0',
+ `link_open` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `link_class` varchar(255) NOT NULL DEFAULT '0',
+ `link_function` varchar(100) NOT NULL DEFAULT '',
+ `link_sefurl` varchar(255) NOT NULL,
+ `link_owner` varchar(50) NOT NULL DEFAULT '',
+ PRIMARY KEY (`link_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_links`
+--
+
+LOCK TABLES `e107_links` WRITE;
+/*!40000 ALTER TABLE `e107_links` DISABLE KEYS */;
+INSERT INTO `e107_links` VALUES (1,'Home','index.php','','',1,0,0,0,'0','','',''),(3,'Members','user.php','','icon-user.glyph',2,3,0,0,'0','','',''),(4,'Submit News','submitnews.php','','icon-upload.glyph',2,4,0,0,'253','','',''),(5,'Contact Us','contact.php','','',1,5,0,0,'0','','',''),(6,'News','news.php','','',1,1,0,0,'0','','index','news'),(7,'Content','page.php','','',1,2,0,0,'0','','',''),(8,'Article 1','page.php?id=1','','',1,0,7,0,'0','','',''),(9,'Article 2','page.php?id=2','','',1,1,7,0,'0','','',''),(10,'Article 3','page.php?id=3','','',1,2,7,0,'0','','',''),(11,'Gallery','{e_PLUGIN}gallery/gallery.php','','',1,10,0,0,'0','','index','gallery');
+/*!40000 ALTER TABLE `e107_links` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_mail_content`
+--
+
+DROP TABLE IF EXISTS `e107_mail_content`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_mail_content` (
+ `mail_source_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `mail_content_status` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `mail_total_count` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_togo_count` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_sent_count` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_fail_count` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_bounce_count` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_start_send` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_end_send` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_create_date` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_creator` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_create_app` varchar(20) NOT NULL DEFAULT '',
+ `mail_e107_priority` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `mail_notify_complete` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `mail_last_date` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_title` varchar(100) NOT NULL DEFAULT '',
+ `mail_subject` varchar(100) NOT NULL DEFAULT '',
+ `mail_body` text,
+ `mail_body_templated` text,
+ `mail_other` text,
+ `mail_media` text,
+ PRIMARY KEY (`mail_source_id`),
+ KEY `mail_content_status` (`mail_content_status`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_mail_content`
+--
+
+LOCK TABLES `e107_mail_content` WRITE;
+/*!40000 ALTER TABLE `e107_mail_content` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_mail_content` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_mail_recipients`
+--
+
+DROP TABLE IF EXISTS `e107_mail_recipients`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_mail_recipients` (
+ `mail_target_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `mail_recipient_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_recipient_email` varchar(80) NOT NULL DEFAULT '',
+ `mail_recipient_name` varchar(80) NOT NULL DEFAULT '',
+ `mail_status` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `mail_detail_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_send_date` int(10) unsigned NOT NULL DEFAULT '0',
+ `mail_target_info` text,
+ PRIMARY KEY (`mail_target_id`),
+ KEY `mail_status` (`mail_status`),
+ KEY `mail_detail_id` (`mail_detail_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_mail_recipients`
+--
+
+LOCK TABLES `e107_mail_recipients` WRITE;
+/*!40000 ALTER TABLE `e107_mail_recipients` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_mail_recipients` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_menus`
+--
+
+DROP TABLE IF EXISTS `e107_menus`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_menus` (
+ `menu_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `menu_name` varchar(100) NOT NULL DEFAULT '',
+ `menu_location` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `menu_order` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `menu_class` varchar(255) NOT NULL DEFAULT '0',
+ `menu_pages` text NOT NULL,
+ `menu_path` varchar(100) NOT NULL DEFAULT '',
+ `menu_layout` varchar(100) NOT NULL DEFAULT '',
+ `menu_parms` text NOT NULL,
+ PRIMARY KEY (`menu_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=38 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_menus`
+--
+
+LOCK TABLES `e107_menus` WRITE;
+/*!40000 ALTER TABLE `e107_menus` DISABLE KEYS */;
+INSERT INTO `e107_menus` VALUES (1,'login_menu',1,1,'0','','login_menu/','',''),(2,'online_menu',0,0,'0','','online/','',''),(3,'blogcalendar_menu',0,0,'0','','blogcalendar_menu/','',''),(5,'search_menu',0,0,'0','','search_menu/','',''),(6,'counter_menu',0,0,'0','','siteinfo/','',''),(7,'userlanguage_menu',0,0,'0','','user/','',''),(8,'compliance_menu',0,0,'0','','siteinfo/','',''),(9,'other_news_menu',0,0,'0','','news/','',''),(10,'clock_menu',0,0,'0','','clock_menu/','',''),(11,'other_news2_menu',0,0,'0','','news/','',''),(12,'comment_menu',0,0,'0','','comment_menu/','',''),(13,'lastseen_menu',0,0,'0','','online/','',''),(14,'news_months_menu',1,2,'0','','news/','',''),(15,'admin_menu',0,0,'0','','admin_menu/','',''),(16,'news_categories_menu',0,0,'0','','news/','',''),(17,'latestnews_menu',0,0,'0','','news/','',''),(18,'usertheme_menu',0,0,'0','','user/','',''),(19,'powered_by_menu',0,0,'0','','siteinfo/','',''),(20,'sitebutton_menu',0,0,'0','','siteinfo/','',''),(21,'news_carousel_menu',0,0,'0','','news/','',''),(22,'chapter_menu',0,0,'0','','page/','',''),(23,'page_menu',0,0,'0','','page/','',''),(24,'page_navigation_menu',0,0,'0','','page/','',''),(25,'featurebox_menu',0,0,'0','0','featurebox/','',''),(26,'slideshow_menu',0,0,'0','0','gallery/','',''),(27,'rss_menu',0,0,'0','0','rss_menu/','',''),(28,'fb_like_menu',0,0,'0','0','social/','',''),(29,'twitter_menu',0,0,'0','0','social/','',''),(30,'jumbotron-menu-2',0,0,'0','','2','',''),(31,'jumbotron-menu-3',0,0,'0','','3','',''),(32,'feature-menu-1',0,0,'0','','4','',''),(33,'feature-menu-2',0,0,'0','','5','',''),(34,'feature-menu-3',0,0,'0','','6','',''),(35,'jumbotron-menu-1',2,1,'','','1','jumbotron_home',''),(36,'jumbotron-menu-2',3,1,'','','2','jumbotron_home',''),(37,'jumbotron-menu-3',4,1,'','','3','jumbotron_home','');
+/*!40000 ALTER TABLE `e107_menus` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_news`
+--
+
+DROP TABLE IF EXISTS `e107_news`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_news` (
+ `news_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `news_title` varchar(255) NOT NULL DEFAULT '',
+ `news_sef` varchar(200) NOT NULL DEFAULT '',
+ `news_body` longtext NOT NULL,
+ `news_extended` longtext NOT NULL,
+ `news_meta_keywords` varchar(255) NOT NULL DEFAULT '',
+ `news_meta_description` text NOT NULL,
+ `news_datestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `news_author` int(10) unsigned NOT NULL DEFAULT '0',
+ `news_category` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `news_allow_comments` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `news_start` int(10) unsigned NOT NULL DEFAULT '0',
+ `news_end` int(10) unsigned NOT NULL DEFAULT '0',
+ `news_class` varchar(255) NOT NULL DEFAULT '0',
+ `news_render_type` varchar(20) NOT NULL DEFAULT '0',
+ `news_comment_total` int(10) unsigned NOT NULL DEFAULT '0',
+ `news_summary` text NOT NULL,
+ `news_thumbnail` text NOT NULL,
+ `news_sticky` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `news_template` varchar(50) DEFAULT NULL,
+ PRIMARY KEY (`news_id`),
+ KEY `news_category` (`news_category`),
+ KEY `news_start_end` (`news_start`,`news_end`),
+ KEY `news_datestamp` (`news_datestamp`),
+ KEY `news_sticky` (`news_sticky`),
+ KEY `news_render_type` (`news_render_type`),
+ KEY `news_class` (`news_class`)
+) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_news`
+--
+
+LOCK TABLES `e107_news` WRITE;
+/*!40000 ALTER TABLE `e107_news` DISABLE KEYS */;
+INSERT INTO `e107_news` VALUES (1,'Welcome to e107','welcome-to-e107','[html]
Lorem ipsum dolor sit amet, no meis semper dicunt est, petentium eloquentiam quo ne. At vero facer eam. Ex nam altera oportere, nisl natum prima id pro. Rebum augue dissentiet eum te, vel veniam eirmod option ea, at eos velit repudiare. Ius sumo dicit adolescens id, an cum efficiantur concludaturque.
Summo sensibus cum ne, et duo torquatos conceptam. No aeque elitr constituam qui. Nostro corpora nec no, diam verterem tincidunt has et. Altera accumsan urbanitas pro eu, ei assum voluptaria sed. Eam tibique nominavi consequuntur an.
Ei perfecto delicata usu, quo eius noster blandit te. Eu doctus volumus pri. Meis argumentum an nam, eos odio prima autem an. Te complectitur intellegebat pro, ius id alterum maiestatis. Ea facer accusata sed, ex illum antiopam quo.
Altera putent pri ad, in phaedrum dissentiunt per. Te eum everti dolores. Ut mea vero autem viderer, mel brute harum senserit id. Minim senserit eloquentiam duo in, sit ei justo graece petentium. Sea id homero oporteat invenire.
Pri semper dolorum ad. Cu eius repudiare eos. Eum in eleifend necessitatibus. Ne has mutat intellegebat.
[/html]','[html]
Lorem ipsum is a pseudo-Latin text used in web design, typography, layout, and printing in place of English to emphasise design elements over content. It's also called placeholder (or filler) text. It's a convenient tool for mock-ups. It helps to outline the visual elements of a document or presentation, eg typography, font, or layout. Lorem ipsum is mostly a part of a Latin text by the classical author and philosopher Cicero. Its words and letters have been changed by addition or removal, so to deliberately render its content nonsensical; it's not genuine, correct, or comprehensible Latin anymore.
\n
While lorem ipsum's still resembles classical Latin, it actually has no meaning whatsoever. As Cicero's text doesn't contain the letters K, W, or Z, alien to latin, these, and others are often inserted randomly to mimic the typographic appearence of European languages, as are digraphs not to be found in the original.
[/html]','welcome,new website','Description for Facebook and search engines.',1454367600,1,1,0,0,0,'0','0',0,'Summary of the news item','',0,NULL);
+/*!40000 ALTER TABLE `e107_news` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_news_category`
+--
+
+DROP TABLE IF EXISTS `e107_news_category`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_news_category` (
+ `category_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
+ `category_name` varchar(200) NOT NULL DEFAULT '',
+ `category_sef` varchar(200) NOT NULL DEFAULT '',
+ `category_meta_description` text NOT NULL,
+ `category_meta_keywords` varchar(255) NOT NULL DEFAULT '',
+ `category_manager` tinyint(3) unsigned NOT NULL DEFAULT '254',
+ `category_icon` varchar(250) NOT NULL DEFAULT '',
+ `category_order` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `category_template` varchar(50) DEFAULT NULL,
+ PRIMARY KEY (`category_id`),
+ KEY `category_order` (`category_order`)
+) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_news_category`
+--
+
+LOCK TABLES `e107_news_category` WRITE;
+/*!40000 ALTER TABLE `e107_news_category` DISABLE KEYS */;
+INSERT INTO `e107_news_category` VALUES (1,'Misc','misc','','',254,'news_16.png',0,NULL);
+/*!40000 ALTER TABLE `e107_news_category` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_online`
+--
+
+DROP TABLE IF EXISTS `e107_online`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_online` (
+ `online_timestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `online_flag` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `online_user_id` varchar(100) NOT NULL DEFAULT '',
+ `online_ip` varchar(45) NOT NULL DEFAULT '',
+ `online_location` text NOT NULL,
+ `online_pagecount` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `online_active` int(10) unsigned NOT NULL DEFAULT '0',
+ `online_agent` varchar(255) NOT NULL DEFAULT '',
+ `online_language` varchar(2) NOT NULL DEFAULT '',
+ KEY `online_ip` (`online_ip`),
+ KEY `online_ip_user_id` (`online_ip`,`online_user_id`),
+ KEY `online_timestamp` (`online_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_online`
+--
+
+LOCK TABLES `e107_online` WRITE;
+/*!40000 ALTER TABLE `e107_online` DISABLE KEYS */;
+INSERT INTO `e107_online` VALUES (1541074256,0,'0','0000:0000:0000:0000:0000:ffff:0a40:e501','/e107/index.php',1,0,'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0','en');
+/*!40000 ALTER TABLE `e107_online` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_page`
+--
+
+DROP TABLE IF EXISTS `e107_page`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_page` (
+ `page_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `page_title` varchar(250) NOT NULL DEFAULT '',
+ `page_sef` varchar(250) NOT NULL DEFAULT '',
+ `page_chapter` int(10) unsigned NOT NULL DEFAULT '0',
+ `page_metakeys` varchar(250) NOT NULL DEFAULT '',
+ `page_metadscr` mediumtext,
+ `page_text` mediumtext,
+ `page_author` int(10) unsigned NOT NULL DEFAULT '0',
+ `page_datestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `page_rating_flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `page_comment_flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `page_password` varchar(50) NOT NULL DEFAULT '',
+ `page_class` varchar(250) NOT NULL DEFAULT '0',
+ `page_ip_restrict` text,
+ `page_template` varchar(50) NOT NULL DEFAULT '',
+ `page_order` int(4) unsigned NOT NULL DEFAULT '9999',
+ `page_fields` mediumtext,
+ `menu_name` varchar(50) NOT NULL DEFAULT '',
+ `menu_title` varchar(250) NOT NULL DEFAULT '',
+ `menu_text` mediumtext,
+ `menu_image` varchar(250) NOT NULL DEFAULT '',
+ `menu_icon` varchar(250) NOT NULL DEFAULT '',
+ `menu_template` varchar(50) NOT NULL DEFAULT '',
+ `menu_class` varchar(250) NOT NULL DEFAULT '0',
+ `menu_button_url` varchar(250) NOT NULL DEFAULT '',
+ `menu_button_text` varchar(250) NOT NULL DEFAULT '',
+ PRIMARY KEY (`page_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_page`
+--
+
+LOCK TABLES `e107_page` WRITE;
+/*!40000 ALTER TABLE `e107_page` DISABLE KEYS */;
+INSERT INTO `e107_page` VALUES (1,'Article 1','article-1',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero ipsum; imperdiet at risus non, dictum sagittis odio! Nulla facilisi. Pellentesque adipiscing facilisis pharetra. Morbi imperdiet augue in ligula luctus, et iaculis est porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ultricies vitae nisi ut porttitor. Curabitur lectus tellus, feugiat a elit vel, gravida iaculis dui. Nulla vulputate turpis dui, ac faucibus enim dignissim non. Ut non tellus suscipit, scelerisque orci sed, scelerisque sapien. Aenean convallis sodales nulla in porttitor. In pretium ante sapien, a tempor eros blandit nec.\n\n Nulla non est nibh? Fusce lacinia quam adipiscing magna posuere dapibus. Sed mollis condimentum rhoncus. Morbi sollicitudin tellus a ligula luctus, ac varius arcu ullamcorper. Mauris in aliquet tellus, nec porttitor dui. Quisque interdum euismod mi sed bibendum. Vivamus non odio quis quam lacinia rhoncus in nec nibh. Integer vitae turpis condimentum, laoreet diam nec viverra fusce.',1,1371420000,1,1,'','0','','default',9999,'','jumbotron-menu-1','Heading 1','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor egestas condimentum. Suspendisse placerat nunc orci, a ultrices tortor eleifend in. Vestibulum tincidunt fringilla malesuada? Phasellus dolor risus, aliquam eu odio quis, mattis cursus magna. Integer ut blandit purus; vitae posuere ante. Vivamus sapien nisl, pulvinar vel turpis a, malesuada vehicula lorem! Curabitur magna justo; laoreet at congue sit amet, tincidunt sit amet erat. Integer vehicula eros quis odio tincidunt, nec dapibus sem molestie. Cras sed viverra eros. Nulla ut lectus tellus.','','','button','0','',''),(2,'Article 2','article-2',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a nunc eget nulla rutrum fringilla sed vel nisl. Suspendisse at erat a neque pulvinar sodales. Etiam dictum erat erat. Pellentesque dictum convallis massa, eu varius sapien gravida a. Suspendisse auctor, mi a tempor tincidunt, lorem felis gravida libero, nec mattis eros felis eu neque. Etiam vel sagittis ipsum. Morbi at libero accumsan mauris tincidunt adipiscing sed vitae mauris. Aenean suscipit consectetur dignissim. Aliquam ornare diam vitae libero suscipit eget facilisis nisl ultrices? Curabitur luctus turpis ac lorem iaculis id interdum massa blandit! Curabitur placerat massa at neque accumsan porta. Nulla sed tortor id sapien dapibus auctor at eget mi. Sed ultrices lacinia viverra.Mauris in purus mauris. Nulla facilisi. Suspendisse dui enim, dapibus sed accumsan non, gravida eget nulla. Praesent ullamcorper, est ac viverra congue, nisl lacus imperdiet sem, et laoreet velit mauris sed leo. Donec erat mi, luctus quis suscipit pretium, luctus sed orci. Proin eu ultricies sem. Nam mollis turpis non erat fermentum congue.Nullam adipiscing, purus et varius blandit, arcu nisi pretium urna, a euismod eros eros quis turpis! Aliquam quis mi turpis, in varius neque. Aliquam erat volutpat. Vestibulum venenatis ipsum dapibus ante dictum egestas. Pellentesque eget nisi ligula, id accumsan eros. Aenean vitae felis vitae sapien mattis pellentesque non nec dolor. Nulla facilisi.Quisque in tortor turpis, sed venenatis nunc. Nunc tellus elit, suscipit et accumsan ac, mollis et risus. Phasellus consectetur sapien in felis semper non tempor tortor mollis. Ut id leo vel magna rhoncus aliquet. Nunc eget sollicitudin felis. Vestibulum tempor, justo et congue dignissim, augue ipsum malesuada libero, id suscipit felis mi vitae erat. Etiam suscipit lacinia sapien, ac gravida nisi lobortis non. Suspendisse dictum risus in est egestas egestas. Aenean et nisi sed odio sollicitudin placerat vel ut magna. Nulla facilisi. Proin condimentum ultricies vulputate. Morbi dapibus elit a tellus elementum congue! In vitae aliquet leo. Proin tempus tincidunt magna, sit amet malesuada ipsum ornare sed. Sed lorem augue, aliquam sit amet hendrerit sed; venenatis in massa. Sed iaculis amet.',1,1366149600,0,0,'','0','','default',9999,'','jumbotron-menu-2','Heading 2','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor egestas condimentum. Suspendisse placerat nunc orci, a ultrices tortor eleifend in. Vestibulum tincidunt fringilla malesuada? Phasellus dolor risus, aliquam eu odio quis, mattis cursus magna. Integer ut blandit purus; vitae posuere ante. Vivamus sapien nisl, pulvinar vel turpis a, malesuada vehicula lorem! Curabitur magna justo; laoreet at congue sit amet, tincidunt sit amet erat. Integer vehicula eros quis odio tincidunt, nec dapibus sem molestie. Cras sed viverra eros. Nulla ut lectus tellus.','','','button','0','',''),(3,'Article 3','article-3',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor a justo ut tristique. Nam molestie sem at leo lobortis, vel luctus ligula tempus. Sed sagittis rutrum lectus vel suscipit. Sed in ligula vitae massa sagittis adipiscing vitae vel sem. Fusce nec rhoncus purus. Aenean mollis magna tellus; non consequat leo feugiat eu. Donec mauris velit, euismod nec facilisis quis, hendrerit nec lorem. Aliquam et lorem sed enim tempus sodales. Vestibulum ut enim adipiscing ipsum semper eleifend in in dolor. Fusce tempor risus velit, in fringilla tellus faucibus eget. Maecenas aliquet nunc nec nulla auctor, sed tristique est pellentesque. Maecenas tristique blandit dolor, nec egestas ligula aliquam eu.\n\nMorbi porta leo iaculis, hendrerit lorem interdum, porta dui. Integer tempor convallis quam, eget tempus neque blandit sit amet. Quisque dictum libero elit, ut vehicula lectus accumsan a. Nam laoreet vulputate massa vel viverra. Sed tempus blandit sapien, in viverra urna aliquet quis. Aliquam erat volutpat. Nulla accumsan posuere.',1,1336600800,0,0,'','0','','default',9999,'','jumbotron-menu-3','Heading 3','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor egestas condimentum. Suspendisse placerat nunc orci, a ultrices tortor eleifend in. Vestibulum tincidunt fringilla malesuada? Phasellus dolor risus, aliquam eu odio quis, mattis cursus magna. Integer ut blandit purus; vitae posuere ante. Vivamus sapien nisl, pulvinar vel turpis a, malesuada vehicula lorem! Curabitur magna justo; laoreet at congue sit amet, tincidunt sit amet erat. Integer vehicula eros quis odio tincidunt, nec dapibus sem molestie. Cras sed viverra eros. Nulla ut lectus tellus.','','','button','0','',''),(5,'Feature 1','feature-1',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor a justo ut tristique. Nam molestie sem at leo lobortis, vel luctus ligula tempus. Sed sagittis rutrum lectus vel suscipit. Sed in ligula vitae massa sagittis adipiscing vitae vel sem. Fusce nec rhoncus purus. Aenean mollis magna tellus; non consequat leo feugiat eu. Donec mauris velit, euismod nec facilisis quis, hendrerit nec lorem. Aliquam et lorem sed enim tempus sodales. Vestibulum ut enim adipiscing ipsum semper eleifend in in dolor. Fusce tempor risus velit, in fringilla tellus faucibus eget. Maecenas aliquet nunc nec nulla auctor, sed tristique est pellentesque. Maecenas tristique blandit dolor, nec egestas ligula aliquam eu.\n\nMorbi porta leo iaculis, hendrerit lorem interdum, porta dui. Integer tempor convallis quam, eget tempus neque blandit sit amet. Quisque dictum libero elit, ut vehicula lectus accumsan a. Nam laoreet vulputate massa vel viverra. Sed tempus blandit sapien, in viverra urna aliquet quis. Aliquam erat volutpat. Nulla accumsan posuere.',1,1344549600,0,0,'','0','','default',9999,'','feature-menu-1','Modern Business Feature Menu 1','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla iaculis odio sit amet mi consequat varius. Nullam fringilla sodales lacus id mollis. Morbi fermentum turpis quis orci scelerisque, non pellentesque dolor sodales. Nullam luctus ut tellus quis ullamcorper! Suspendisse convallis tortor nec semper condimentum. Proin adipiscing adipiscing elit, ut posuere mauris consequat sed. Maecenas ultrices nisi tortor, sed vehicula urna auctor euismod?','','','2-column_1:1_text-left','0','',''),(6,'Feature 2','feature-2',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor a justo ut tristique. Nam molestie sem at leo lobortis, vel luctus ligula tempus. Sed sagittis rutrum lectus vel suscipit. Sed in ligula vitae massa sagittis adipiscing vitae vel sem. Fusce nec rhoncus purus. Aenean mollis magna tellus; non consequat leo feugiat eu. Donec mauris velit, euismod nec facilisis quis, hendrerit nec lorem. Aliquam et lorem sed enim tempus sodales. Vestibulum ut enim adipiscing ipsum semper eleifend in in dolor. Fusce tempor risus velit, in fringilla tellus faucibus eget. Maecenas aliquet nunc nec nulla auctor, sed tristique est pellentesque. Maecenas tristique blandit dolor, nec egestas ligula aliquam eu.\n\nMorbi porta leo iaculis, hendrerit lorem interdum, porta dui. Integer tempor convallis quam, eget tempus neque blandit sit amet. Quisque dictum libero elit, ut vehicula lectus accumsan a. Nam laoreet vulputate massa vel viverra. Sed tempus blandit sapien, in viverra urna aliquet quis. Aliquam erat volutpat. Nulla accumsan posuere.',1,1347228000,0,0,'','0','','default',9999,'','feature-menu-2','Modern Business Feature Menu 2','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla iaculis odio sit amet mi consequat varius. Nullam fringilla sodales lacus id mollis. Morbi fermentum turpis quis orci scelerisque, non pellentesque dolor sodales. Nullam luctus ut tellus quis ullamcorper! Suspendisse convallis tortor nec semper condimentum. Proin adipiscing adipiscing elit, ut posuere mauris consequat sed. Maecenas ultrices nisi tortor, sed vehicula urna auctor euismod?','','','2-column_1:1_text-right','0','',''),(7,'Feature 3','feature-3',2,'','','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor a justo ut tristique. Nam molestie sem at leo lobortis, vel luctus ligula tempus. Sed sagittis rutrum lectus vel suscipit. Sed in ligula vitae massa sagittis adipiscing vitae vel sem. Fusce nec rhoncus purus. Aenean mollis magna tellus; non consequat leo feugiat eu. Donec mauris velit, euismod nec facilisis quis, hendrerit nec lorem. Aliquam et lorem sed enim tempus sodales. Vestibulum ut enim adipiscing ipsum semper eleifend in in dolor. Fusce tempor risus velit, in fringilla tellus faucibus eget. Maecenas aliquet nunc nec nulla auctor, sed tristique est pellentesque. Maecenas tristique blandit dolor, nec egestas ligula aliquam eu.\n\nMorbi porta leo iaculis, hendrerit lorem interdum, porta dui. Integer tempor convallis quam, eget tempus neque blandit sit amet. Quisque dictum libero elit, ut vehicula lectus accumsan a. Nam laoreet vulputate massa vel viverra. Sed tempus blandit sapien, in viverra urna aliquet quis. Aliquam erat volutpat. Nulla accumsan posuere.',1,1349820000,0,0,'','0','','default',9999,'','feature-menu-3',''Modern Business' is a ready-to-use, Bootstrap 3 updated, multi-purpose HTML theme!','For more templates and more page options that you can integrate into this website template, visit Start Bootstrap!','','','2-column_2:1_text-left','0','',''),(4,'Article 4','article-4',3,'','','[html]
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor a justo ut tristique. Nam molestie sem at leo lobortis, vel luctus ligula tempus. Sed sagittis rutrum lectus vel suscipit. Sed in ligula vitae massa sagittis adipiscing vitae vel sem. Fusce nec rhoncus purus. Aenean mollis magna tellus; non consequat leo feugiat eu. Donec mauris velit, euismod nec facilisis quis, hendrerit nec lorem. Aliquam et lorem sed enim tempus sodales. Vestibulum ut enim adipiscing ipsum semper eleifend in in dolor. Fusce tempor risus velit, in fringilla tellus faucibus eget. Maecenas aliquet nunc nec nulla auctor, sed tristique est pellentesque. Maecenas tristique blandit dolor, nec egestas ligula aliquam eu. Morbi porta leo iaculis, hendrerit lorem interdum, porta dui. Integer tempor convallis quam, eget tempus neque blandit sit amet. Quisque dictum libero elit, ut vehicula lectus accumsan a. Nam laoreet vulputate massa vel viverra. Sed tempus blandit sapien, in viverra urna aliquet quis. Aliquam erat volutpat. Nulla accumsan posuere.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor egestas condimentum. Suspendisse placerat nunc orci, a ultrices tortor eleifend in. Vestibulum tincidunt fringilla malesuada? Phasellus dolor risus, aliquam eu odio quis, mattis cursus magna. Integer ut blandit purus; vitae posuere ante. Vivamus sapien nisl, pulvinar vel turpis a, malesuada vehicula lorem! Curabitur magna justo; laoreet at congue sit amet, tincidunt sit amet erat. Integer vehicula eros quis odio tincidunt, nec dapibus sem molestie. Cras sed viverra eros. Nulla ut lectus tellus.
[/html]','','','button','0','','');
+/*!40000 ALTER TABLE `e107_page` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_page_chapters`
+--
+
+DROP TABLE IF EXISTS `e107_page_chapters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_page_chapters` (
+ `chapter_id` int(4) unsigned NOT NULL AUTO_INCREMENT,
+ `chapter_parent` int(4) unsigned NOT NULL DEFAULT '0',
+ `chapter_name` varchar(200) NOT NULL DEFAULT '',
+ `chapter_sef` varchar(200) NOT NULL DEFAULT '',
+ `chapter_meta_description` text NOT NULL,
+ `chapter_meta_keywords` varchar(255) NOT NULL DEFAULT '',
+ `chapter_manager` tinyint(3) unsigned NOT NULL DEFAULT '254',
+ `chapter_icon` varchar(250) NOT NULL DEFAULT '',
+ `chapter_image` varchar(250) NOT NULL DEFAULT '',
+ `chapter_order` int(6) unsigned NOT NULL DEFAULT '0',
+ `chapter_template` varchar(50) NOT NULL DEFAULT '',
+ `chapter_visibility` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `chapter_fields` mediumtext,
+ PRIMARY KEY (`chapter_id`),
+ KEY `chapter_order` (`chapter_order`)
+) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_page_chapters`
+--
+
+LOCK TABLES `e107_page_chapters` WRITE;
+/*!40000 ALTER TABLE `e107_page_chapters` DISABLE KEYS */;
+INSERT INTO `e107_page_chapters` VALUES (1,0,'General','general','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec et tempor odio. Quisque volutpat lorem nec lectus congue suscipit. In hac habitasse platea dictumst. Etiam odio nisi, egestas vitae amet.','',254,'','',0,'',0,''),(2,1,'Chapter 1','chapter-1','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ut nunc ac neque egestas ullamcorper. In convallis semper hendrerit. Etiam non dolor nisl, varius facilisis dui. Nunc egestas massa nunc.','',254,'','',1,'',0,''),(3,1,'Custom Fields','customfields','Chapter containing custom fields','',254,'','',2,'default',0,'{\n \"__tabs__\": {\n \"additional\": \"Custom Fields\"\n },\n \"mybbarea\": {\n \"title\": \"Rich Text\",\n \"type\": \"bbarea\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myboolean\": {\n \"title\": \"Boolean\",\n \"type\": \"boolean\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mycheckbox\": {\n \"title\": \"Checkbox\",\n \"type\": \"checkbox\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mycountry\": {\n \"title\": \"Country\",\n \"type\": \"country\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mydatestamp\": {\n \"title\": \"Date\",\n \"type\": \"datestamp\",\n \"writeParms\": \"{ \\\"format\\\": \\\"yyyy-mm-dd\\\" }\",\n \"help\": \"\"\n },\n \"mydropdown\": {\n \"title\": \"Selection\",\n \"type\": \"dropdown\",\n \"writeParms\": \"{ \\\"optArray\\\": { \\\"blue\\\": \\\"Blue\\\", \\\"green\\\": \\\"Green\\\", \\\"red\\\": \\\"Red\\\" }, \\\"default\\\": \\\"blank\\\" }\",\n \"help\": \"\"\n },\n \"myemail\": {\n \"title\": \"Email\",\n \"type\": \"email\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myfile\": {\n \"title\": \"File\",\n \"type\": \"file\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myicon\": {\n \"title\": \"Icon\",\n \"type\": \"icon\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myimage\": {\n \"title\": \"Image\",\n \"type\": \"image\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mylanguage\": {\n \"title\": \"Language\",\n \"type\": \"language\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mynumber\": {\n \"title\": \"Number\",\n \"type\": \"number\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myprogressbar\": {\n \"title\": \"Progress\",\n \"type\": \"progressbar\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mytags\": {\n \"title\": \"Tags\",\n \"type\": \"tags\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"mytext\": {\n \"title\": \"Text\",\n \"type\": \"text\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myurl\": {\n \"title\": \"URL\",\n \"type\": \"url\",\n \"writeParms\": \"\",\n \"help\": \"\"\n },\n \"myvideo\": {\n \"title\": \"Video\",\n \"type\": \"video\",\n \"writeParms\": \"\",\n \"help\": \"\"\n }\n}');
+/*!40000 ALTER TABLE `e107_page_chapters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_plugin`
+--
+
+DROP TABLE IF EXISTS `e107_plugin`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_plugin` (
+ `plugin_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `plugin_name` varchar(100) NOT NULL DEFAULT '',
+ `plugin_version` varchar(10) NOT NULL DEFAULT '',
+ `plugin_path` varchar(100) NOT NULL DEFAULT '',
+ `plugin_installflag` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `plugin_addons` text NOT NULL,
+ `plugin_category` varchar(100) NOT NULL DEFAULT '',
+ PRIMARY KEY (`plugin_id`),
+ UNIQUE KEY `plugin_path` (`plugin_path`)
+) ENGINE=MyISAM AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_plugin`
+--
+
+LOCK TABLES `e107_plugin` WRITE;
+/*!40000 ALTER TABLE `e107_plugin` DISABLE KEYS */;
+INSERT INTO `e107_plugin` VALUES (1,'LAN_PLUGIN_POLL_NAME','2.0','poll',0,'poll_sql','content'),(2,'LAN_PLUGIN_TRACKBACK_NAME','1.1','trackback',0,'e_admin,e_meta,trackback_sql','misc'),(3,'LAN_PLUGIN_LINKWORDS_NAME','1.1','linkwords',0,'e_header,e_tohtml,linkwords_sql','content'),(4,'LAN_PLUGIN__BLANK_NAME','2.0','_blank',0,'e_admin,e_cron,e_notify,e_frontpage,e_menu,e_search,e_shortcode,e_event,e_dashboard,e_header,e_url,e_sitelink,e_parse,e_related,e_rss,e_user,e_library,blank_sql','misc'),(5,'LAN_PLUGIN_IMPORT_NAME','1.0','import',0,'','tools'),(6,'LAN_PLUGIN_PM_NAME','3.0','pm',0,'e_cron,e_shortcode,e_url,pm_sql','users'),(7,'Pages','1.0','page',1,'e_menu,e_search,e_shortcode,e_sitelink,e_related','menu'),(8,'LAN_PLUGIN_BANNER_NAME','1.0','banner',0,'e_menu,e_shortcode,banner_sql','content'),(9,'LAN_PLUGIN_RSS_NAME','1.3','rss_menu',1,'e_meta,e_url,rss_sql','misc'),(10,'TinyMce4','1.0','tinymce4',1,'e_header,e_footer','misc'),(11,'LAN_PLUGIN_TAGCLOUD_NAME','1.3','tagcloud',0,'e_menu,e_header','misc'),(12,'LAN_PLUGIN_USER_NAME','1.0','user',1,'e_dashboard,e_mailout,e_user','misc'),(13,'Metaweblog','1.0','metaweblog',0,'e_header','tools'),(14,'Contact','1.0','contact',1,'','menu'),(15,'New Forum Posts','1','newforumposts_main',0,'newforumposts.sc','misc'),(16,'LAN_PLUGIN_FORUM_NAME','2.1','forum',0,'e_notify,e_linkgen,e_list,e_meta,e_emailprint,e_frontpage,e_menu,e_search,e_event,e_dashboard,e_url,e_rss,e_user,e_gsitemap,forum_sql','content'),(17,'LAN_PLUGIN_SOCIAL_NAME','1.0','social',1,'e_admin,e_shortcode,e_module,e_event,e_comment','settings'),(18,'News','1.0','news',1,'e_menu,e_search,e_event,e_header,e_url,e_sitelink,e_featurebox,e_related,e_rss,e_gsitemap','menu'),(19,'LAN_PLUGIN_CHATBOX_MENU_NAME','1.0','chatbox_menu',0,'e_notify,e_list,e_search,e_dashboard,e_header,e_rss,e_user,chatbox_sql','content'),(20,'LAN_PLUGIN_LOG_NAME','2.1','log',0,'e_meta,e_shortcode,e_url,log_sql','misc'),(21,'LAN_PLUGIN_GALLERY_TITLE','1.1','gallery',1,'e_shortcode,e_header,e_url,e_library','content'),(22,'LAN_PLUGIN_FEATUREBOX_NAME','1.1','featurebox',1,'e_shortcode,e_header,e_rss,featurebox_sql','content'),(23,'Siteinfo','1.0','siteinfo',1,'e_shortcode','menu'),(24,'LAN_PLUGIN_NEWSLETTER_NAME','1.1','newsletter',0,'e_mailout,newsletter_sql','content'),(25,'LAN_ALT_65','0.4','alt_auth',0,'alt_auth_sql','users'),(26,'LAN_PLUGIN_GSITEMAP_NAME','1.0','gsitemap',0,'e_cron,e_module,e_url,gsitemap_sql','misc'),(27,'LAN_PLUGIN_DOWNLOAD_NAME','1.2','download',0,'e_cron,e_list,e_frontpage,e_search,e_tagwords,e_url,e_sitelink,e_rss,e_upload,e_gsitemap,download_sql','content'),(28,'LAN_PLUGIN_NEWSFEEDS_NAME','2.0','newsfeed',0,'e_frontpage,e_url,newsfeed_sql','content'),(29,'LAN_PLUGIN_LIST_NEW_NAME','1.0.1','list_new',0,'','content'),(30,'LAN_PLUGIN_FAQS_NAME','1.1','faqs',0,'e_cron,e_meta,e_search,e_url,e_sitelink,faqs_sql','content');
+/*!40000 ALTER TABLE `e107_plugin` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_rate`
+--
+
+DROP TABLE IF EXISTS `e107_rate`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_rate` (
+ `rate_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `rate_table` varchar(100) NOT NULL DEFAULT '',
+ `rate_itemid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rate_rating` int(10) unsigned NOT NULL DEFAULT '0',
+ `rate_votes` int(10) unsigned NOT NULL DEFAULT '0',
+ `rate_voters` text NOT NULL,
+ `rate_up` int(10) unsigned NOT NULL DEFAULT '0',
+ `rate_down` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`rate_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_rate`
+--
+
+LOCK TABLES `e107_rate` WRITE;
+/*!40000 ALTER TABLE `e107_rate` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_rate` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_rss`
+--
+
+DROP TABLE IF EXISTS `e107_rss`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_rss` (
+ `rss_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `rss_name` varchar(255) NOT NULL DEFAULT '',
+ `rss_url` text NOT NULL,
+ `rss_topicid` varchar(255) NOT NULL DEFAULT '',
+ `rss_path` varchar(255) NOT NULL DEFAULT '',
+ `rss_text` longtext NOT NULL,
+ `rss_datestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `rss_class` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `rss_limit` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`rss_id`),
+ KEY `rss_name` (`rss_name`)
+) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_rss`
+--
+
+LOCK TABLES `e107_rss` WRITE;
+/*!40000 ALTER TABLE `e107_rss` DISABLE KEYS */;
+INSERT INTO `e107_rss` VALUES (1,'News','news','0','0','The rss feed of the news',1456448477,0,10);
+/*!40000 ALTER TABLE `e107_rss` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_submitnews`
+--
+
+DROP TABLE IF EXISTS `e107_submitnews`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_submitnews` (
+ `submitnews_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `submitnews_name` varchar(100) NOT NULL DEFAULT '',
+ `submitnews_email` varchar(100) NOT NULL DEFAULT '',
+ `submitnews_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `submitnews_title` varchar(200) NOT NULL DEFAULT '',
+ `submitnews_category` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `submitnews_item` text NOT NULL,
+ `submitnews_datestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `submitnews_ip` varchar(45) NOT NULL DEFAULT '',
+ `submitnews_auth` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `submitnews_file` text NOT NULL,
+ `submitnews_keywords` varchar(255) NOT NULL DEFAULT '',
+ `submitnews_description` text,
+ `submitnews_summary` text,
+ `submitnews_media` text,
+ PRIMARY KEY (`submitnews_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_submitnews`
+--
+
+LOCK TABLES `e107_submitnews` WRITE;
+/*!40000 ALTER TABLE `e107_submitnews` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_submitnews` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_tmp`
+--
+
+DROP TABLE IF EXISTS `e107_tmp`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_tmp` (
+ `tmp_ip` varchar(45) NOT NULL DEFAULT '',
+ `tmp_time` int(10) unsigned NOT NULL DEFAULT '0',
+ `tmp_info` text NOT NULL,
+ KEY `tmp_ip` (`tmp_ip`),
+ KEY `tmp_time` (`tmp_time`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_tmp`
+--
+
+LOCK TABLES `e107_tmp` WRITE;
+/*!40000 ALTER TABLE `e107_tmp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_tmp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_upload`
+--
+
+DROP TABLE IF EXISTS `e107_upload`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_upload` (
+ `upload_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `upload_poster` varchar(100) NOT NULL DEFAULT '',
+ `upload_email` varchar(100) NOT NULL DEFAULT '',
+ `upload_website` varchar(100) NOT NULL DEFAULT '',
+ `upload_datestamp` int(10) unsigned NOT NULL DEFAULT '0',
+ `upload_name` varchar(100) NOT NULL DEFAULT '',
+ `upload_version` varchar(10) NOT NULL DEFAULT '',
+ `upload_file` varchar(180) NOT NULL DEFAULT '',
+ `upload_ss` varchar(100) NOT NULL DEFAULT '',
+ `upload_description` text NOT NULL,
+ `upload_demo` varchar(100) NOT NULL DEFAULT '',
+ `upload_filesize` int(10) unsigned NOT NULL DEFAULT '0',
+ `upload_active` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `upload_category` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `upload_owner` varchar(50) NOT NULL DEFAULT '',
+ PRIMARY KEY (`upload_id`),
+ KEY `upload_active` (`upload_active`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_upload`
+--
+
+LOCK TABLES `e107_upload` WRITE;
+/*!40000 ALTER TABLE `e107_upload` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_upload` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_user`
+--
+
+DROP TABLE IF EXISTS `e107_user`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_user` (
+ `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `user_name` varchar(100) NOT NULL DEFAULT '',
+ `user_loginname` varchar(100) NOT NULL DEFAULT '',
+ `user_customtitle` varchar(100) NOT NULL DEFAULT '',
+ `user_password` varchar(255) NOT NULL DEFAULT '',
+ `user_sess` varchar(100) NOT NULL DEFAULT '',
+ `user_email` varchar(100) NOT NULL DEFAULT '',
+ `user_signature` text NOT NULL,
+ `user_image` varchar(255) NOT NULL DEFAULT '',
+ `user_hideemail` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_join` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_lastvisit` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_currentvisit` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_lastpost` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_chats` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_comments` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_ip` varchar(45) NOT NULL DEFAULT '',
+ `user_ban` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_prefs` text NOT NULL,
+ `user_visits` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_admin` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_login` varchar(100) NOT NULL DEFAULT '',
+ `user_class` text NOT NULL,
+ `user_perms` text NOT NULL,
+ `user_realm` text NOT NULL,
+ `user_pwchange` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_xup` text,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `user_name` (`user_name`),
+ UNIQUE KEY `user_loginname` (`user_loginname`),
+ KEY `join_ban_index` (`user_join`,`user_ban`)
+) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_user`
+--
+
+LOCK TABLES `e107_user` WRITE;
+/*!40000 ALTER TABLE `e107_user` DISABLE KEYS */;
+INSERT INTO `e107_user` VALUES (1,'e107','e107','','$2y$10$9I/WNzNKiwhwGAugF3kKPeLB8R8gCGUjd9T1rGrHsP46//koUS84.','','e107inc@gmail.com','','',0,1541074253,0,0,0,0,0,'10.64.229.1',0,'',0,1,'','4,5','0','',1541074253,'');
+/*!40000 ALTER TABLE `e107_user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_user_extended`
+--
+
+DROP TABLE IF EXISTS `e107_user_extended`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_user_extended` (
+ `user_extended_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_hidden_fields` text,
+ PRIMARY KEY (`user_extended_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_user_extended`
+--
+
+LOCK TABLES `e107_user_extended` WRITE;
+/*!40000 ALTER TABLE `e107_user_extended` DISABLE KEYS */;
+INSERT INTO `e107_user_extended` VALUES (1,NULL);
+/*!40000 ALTER TABLE `e107_user_extended` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_user_extended_struct`
+--
+
+DROP TABLE IF EXISTS `e107_user_extended_struct`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_user_extended_struct` (
+ `user_extended_struct_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `user_extended_struct_name` varchar(255) NOT NULL DEFAULT '',
+ `user_extended_struct_text` varchar(255) NOT NULL DEFAULT '',
+ `user_extended_struct_type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_parms` varchar(255) NOT NULL DEFAULT '',
+ `user_extended_struct_values` text NOT NULL,
+ `user_extended_struct_default` varchar(255) NOT NULL DEFAULT '',
+ `user_extended_struct_read` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_write` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_required` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_signup` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_applicable` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_order` int(10) unsigned NOT NULL DEFAULT '0',
+ `user_extended_struct_parent` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`user_extended_struct_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_user_extended_struct`
+--
+
+LOCK TABLES `e107_user_extended_struct` WRITE;
+/*!40000 ALTER TABLE `e107_user_extended_struct` DISABLE KEYS */;
+/*!40000 ALTER TABLE `e107_user_extended_struct` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `e107_userclass_classes`
+--
+
+DROP TABLE IF EXISTS `e107_userclass_classes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `e107_userclass_classes` (
+ `userclass_id` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `userclass_name` varchar(100) NOT NULL DEFAULT '',
+ `userclass_description` varchar(250) NOT NULL DEFAULT '',
+ `userclass_editclass` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `userclass_parent` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `userclass_accum` varchar(250) NOT NULL DEFAULT '',
+ `userclass_visibility` smallint(5) NOT NULL DEFAULT '0',
+ `userclass_type` tinyint(1) unsigned NOT NULL DEFAULT '0',
+ `userclass_icon` varchar(250) NOT NULL DEFAULT '',
+ `userclass_perms` text NOT NULL,
+ PRIMARY KEY (`userclass_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `e107_userclass_classes`
+--
+
+LOCK TABLES `e107_userclass_classes` WRITE;
+/*!40000 ALTER TABLE `e107_userclass_classes` DISABLE KEYS */;
+INSERT INTO `e107_userclass_classes` VALUES (2,'CONTACT PEOPLE','Example contact person class',254,0,'0,2',0,0,'',''),(3,'NEWSLETTER','Subscribe to Newsletter',0,255,'3',0,0,'',''),(253,'Members','Registered and logged in members',250,0,'0,253',253,0,'',''),(246,'Search Bots','Identified search bots',250,0,'0,246',254,0,'',''),(254,'Admin','Site Administrators',250,0,'0,254',253,0,'',''),(248,'Forum Moderators','Moderators for Forums and other areas',250,0,'0,248',253,0,'',''),(249,'Admins and Mods','Anyone able to administer something, moderate forums etc',250,0,'0,249',253,0,'',''),(1,'PRIVATEMENU','Grants access to private menu items',254,0,'0,1',0,0,'',''),(247,'New Users','Recently joined users',250,0,'0,247',254,0,'',''),(250,'Main Admin','Main site Administrators',250,0,'0,250',253,0,'',''),(0,'Everyone (public)','Fixed class',250,0,'0',0,0,'',''),(252,'Guests','Fixed class',250,0,'0,252',0,0,'',''),(255,'No One (inactive)','Fixed class',250,0,'255',0,0,'',''),(251,'Read Only','Fixed class',250,0,'0,251',0,0,'',''),(4,'TestImage1','Used for testing userclass image (FA icon)',254,255,'4',254,0,'fa-fire-extinguisher.glyph',''),(5,'TestImage2','Used for testing userclass image (PNG icon)',254,255,'5',254,0,'{e_PLUGIN}tinymce4/images/icon_32.png','');
+/*!40000 ALTER TABLE `e107_userclass_classes` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2018-11-01 12:11:31
diff --git a/e107_tests/tests/_data/eml/bounced_01.eml b/e107_tests/tests/_data/eml/bounced_01.eml
new file mode 100644
index 000000000..002779d2c
--- /dev/null
+++ b/e107_tests/tests/_data/eml/bounced_01.eml
@@ -0,0 +1,37 @@
+Return-path:
+Received: from e107 by secure.awscloudservices.net with local (Exim 4.91)
+ (envelope-from )
+ id 1hG9qV-0002Ux-RO
+ for bounce-newsletter@e107.org; Mon, 15 Apr 2019 15:12:59 -0700
+To: Bounce handler
+Subject: Test Bounce
+X-PHP-Script: www.e107.org/e107_admin/mailout.php for 98.160.237.154
+X-PHP-Originating-Script: 1010:class.phpmailer.php
+Date: Mon, 15 Apr 2019 16:12:59 -0600
+From: "e107.org"
+Message-ID:
+X-Mailer: PHPMailer 5.2.27 (https://github.com/PHPMailer/PHPMailer)
+X-e107-id: 99999999
+X-Bounce-Test: true
+MIME-Version: 1.0
+Content-Type: multipart/alternative;
+ boundary="b1_b32e962004f61de78186edcc7a1c7e2e"
+Content-Transfer-Encoding: 8bit
+
+This is a multi-part message in MIME format.
+
+--b1_b32e962004f61de78186edcc7a1c7e2e
+Content-Type: text/plain; charset=us-ascii
+
+Test Bounce Email address
+
+
+--b1_b32e962004f61de78186edcc7a1c7e2e
+Content-Type: text/html; charset=us-ascii
+
+Test Bounce Email address
+
+
+
+--b1_b32e962004f61de78186edcc7a1c7e2e--
+
diff --git a/e107_tests/tests/_data/icon_64.png b/e107_tests/tests/_data/icon_64.png
new file mode 100644
index 000000000..4c86b4cd5
Binary files /dev/null and b/e107_tests/tests/_data/icon_64.png differ
diff --git a/e107_tests/tests/_data/testcore/calendar_template.php b/e107_tests/tests/_data/testcore/calendar_template.php
new file mode 100644
index 000000000..36ae5f7a0
--- /dev/null
+++ b/e107_tests/tests/_data/testcore/calendar_template.php
@@ -0,0 +1,269 @@
+";
+$sc_style['PREV_MONTH']['post'] = "";
+
+$sc_style['CURRENT_MONTH']['pre'] = "";
+$sc_style['CURRENT_MONTH']['post'] = "";
+
+$sc_style['NEXT_MONTH']['pre'] = "";
+$sc_style['NEXT_MONTH']['post'] = "";
+
+$sc_style['PREV_YEAR']['pre'] = "";
+$sc_style['PREV_YEAR']['post'] = "";
+
+$sc_style['MONTH_LIST']['pre'] = "";
+$sc_style['MONTH_LIST']['post'] = "";
+
+$sc_style['NEXT_YEAR']['pre'] = "";
+$sc_style['NEXT_YEAR']['post'] = "";
+
+//
";
+
+// Start and end CSS for date cells - six cases to decode, determined by array index:
+// 1 - Today, no events
+// 2 - Some other day, no events (or no icon defined)
+// 3 - Today with events (and icon defined)
+// 4 - Some other day with events (and icon defined)
+// 5 - today with events, one or more of which has recently been added/updated (and icon defined)
+// 6 - Some other day with events, one or more of which has recently been added/updated (and icon defined)
+
+//today, no events
+$CALENDAR_MENU_DAY_START['1'] = "
";
+
+// no events and not today
+$CALENDAR_MENU_DAY_START['2'] = "
";
+
+//day has events - same whether its today or not
+$CALENDAR_MENU_DAY_START['3'] = "
";
+$CALENDAR_MENU_DAY_START['4'] = "
";
+// day has events, one which is recently added/updated
+$CALENDAR_MENU_DAY_START['5'] = "
";
+$CALENDAR_MENU_DAY_START['6'] = "
";
+// Example highlight using background colour:
+//$CALENDAR_MENU_DAY_START['5'] = "
";
+//$CALENDAR_MENU_DAY_START['6'] = "
";
+
+$CALENDAR_MENU_DAY_END['1'] = "
";
+$CALENDAR_MENU_DAY_END['2'] = "";
+$CALENDAR_MENU_DAY_END['3'] = "";
+$CALENDAR_MENU_DAY_END['4'] = "";
+$CALENDAR_MENU_DAY_END['5'] = "";
+$CALENDAR_MENU_DAY_END['6'] = "";
+
+//============================================================================
+// Next event menu template
+$sc_style['NEXT_EVENT_TIME']['pre'] = EC_LAN_144;
+$sc_style['NEXT_EVENT_TIME']['post'] = "";
+// Following are original styles
+//$sc_style['NEXT_EVENT_ICON']['pre'] = " ";
+// Following to 'float right' on a larger icon
+$sc_style['NEXT_EVENT_ICON']['pre'] = "";
+
+
+if (!isset($EVENT_CAL_FE_LINE))
+{
+ $EVENT_CAL_FE_LINE = "{NEXT_EVENT_ICON}{NEXT_EVENT_DATE}{NEXT_EVENT_TIME} {NEXT_EVENT_TITLE}{NEXT_EVENT_GAP}";
+}
+
+
+?>
\ No newline at end of file
diff --git a/e107_tests/tests/_data/testcore/comment_menu_template.php b/e107_tests/tests/_data/testcore/comment_menu_template.php
new file mode 100644
index 000000000..0d63db2a2
--- /dev/null
+++ b/e107_tests/tests/_data/testcore/comment_menu_template.php
@@ -0,0 +1,38 @@
+";
+$sc_style['CM_AUTHOR']['post'] = "";
+
+$sc_style['CM_DATESTAMP']['pre'] = " ".CM_L11." ";
+$sc_style['CM_DATESTAMP']['post'] = "";
+
+$sc_style['CM_COMMENT']['pre'] = "";
+$sc_style['CM_COMMENT']['post'] = "
+';
+//Render news categories on the bottom of the page
+$NEWSCAT = '
+
+
+
+
+ {NEWSCATEGORY}
+
+
+
+
+
+
+
+
+ {NEWSCAT_ITEM}
+
+
+
+
+
+
+';
+//Loop for news items in category
+$NEWSCAT_ITEM = '
+
+ {NEWSTITLELINK}
+
+';
+// linkstyle
+// http://wiki.e107.org/?title=Styling_Individual_Sitelink_Menus
+function linkstyle($np_linkstyle) {
+// Common to all styles (for this theme)
+// Common sublink settings
+// NOTE: *any* settings can be customized for sublinks by using
+// 'sub' as a prefix for the setting name. Plus, there's 'subindent'
+// $linkstyleset['sublinkclass'] = 'mysublink2;
+// $linkstyleset['subindent'] = ' ';
+// Now for some per-style setup
+ switch ($np_linkstyle)
+ {
+ case 'toplinks':
+ $linkstyleset['linkdisplay'] = 1;
+ $linkstyleset['prelink'] = "
+';
+?>
\ No newline at end of file
diff --git a/e107_tests/tests/_data/testcore/usersettings_template.php b/e107_tests/tests/_data/testcore/usersettings_template.php
new file mode 100644
index 000000000..668f44298
--- /dev/null
+++ b/e107_tests/tests/_data/testcore/usersettings_template.php
@@ -0,0 +1,213 @@
+
+
+ ";
+
+?>
\ No newline at end of file
diff --git a/e107_tests/tests/_data/testkubrick/ul.sc b/e107_tests/tests/_data/testkubrick/ul.sc
new file mode 100644
index 000000000..99536f0df
--- /dev/null
+++ b/e107_tests/tests/_data/testkubrick/ul.sc
@@ -0,0 +1,43 @@
+global $sql, $link_class, $page,$tp;
+
+$sql -> db_Select('links', '*', "link_category = 1 and link_parent =0 and link_class IN (".USERCLASS_LIST.") ORDER BY link_order ASC");
+//$ulmenu = PRELINK."
"; // Leaf
+$ulmenu = "
"; // Kubrick
+$r='1'; // Needs to be a character - used for access key
+while (($row = $sql -> db_Fetch()) && ($r <= "8"))
+{
+ extract($row);
+ $link_url = $tp->replaceConstants($link_url,TRUE);
+// Check if current page is one of the links - Test from kubrick is better
+ $ltest = (e_QUERY ? e_PAGE."?".e_QUERY : e_PAGE);
+ $rtest=substr(strrchr($link_url, "/"), 1);
+ if (strpos($link_url, '://') === FALSE) { $link_url = e_BASE.$link_url; }
+ if($ltest == $link_url || $rtest == e_PAGE){ $ulclass = '_onpage'; } else { $ulclass = ''; }
+
+ $link_append = '';
+ switch ($link_open)
+ {
+ case 0: // Open in same window
+ break;
+ case 1: // Simple open in new window
+ $link_append = " rel='external'";
+ break;
+ case 4: // 600 x 400 window
+ $link_append = " onclick=\"javascript:open_window('{$link_url}',600,400); return false;\"";
+ break;
+ case 5: // 800 x 600 window
+ $link_append = " onclick=\"javascript:open_window('{$link_url}',800,600); return false;\"";
+ break;
+ }
+ $lname = (defined(trim($link_name))) ? constant(trim($link_name)) : $link_name;
+ $ulmenu .= "